Replace all previously text-based json fields with JSONBlob

This JSONBlob field type is a wrapper around Django's new generic
JSONField, but with the database column type forced to be text.  This
should behave close enough to our old wrapper around
django-jsonfield's JSONField and will avoid needing to do the
out-of-band database migration.
This commit is contained in:
Jeff Bradberry
2022-03-23 17:27:28 -04:00
parent 7b2b979c1b
commit e3f3ab224a
24 changed files with 101 additions and 77 deletions

View File

@@ -4,6 +4,8 @@ from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
from django.conf import settings from django.conf import settings
import awx.main.fields
class Migration(migrations.Migration): class Migration(migrations.Migration):
@@ -17,7 +19,7 @@ class Migration(migrations.Migration):
('created', models.DateTimeField(default=None, editable=False)), ('created', models.DateTimeField(default=None, editable=False)),
('modified', models.DateTimeField(default=None, editable=False)), ('modified', models.DateTimeField(default=None, editable=False)),
('key', models.CharField(max_length=255)), ('key', models.CharField(max_length=255)),
('value', models.JSONField(null=True)), ('value', awx.main.fields.JSONBlob(null=True)),
( (
'user', 'user',
models.ForeignKey(related_name='settings', default=None, editable=False, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True), models.ForeignKey(related_name='settings', default=None, editable=False, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True),

View File

@@ -1,11 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations
import awx.main.fields
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [('conf', '0002_v310_copy_tower_settings')] dependencies = [('conf', '0002_v310_copy_tower_settings')]
operations = [migrations.AlterField(model_name='setting', name='value', field=models.JSONField(null=True))] operations = [migrations.AlterField(model_name='setting', name='value', field=awx.main.fields.JSONBlob(null=True))]

View File

@@ -8,6 +8,7 @@ import json
from django.db import models from django.db import models
# AWX # AWX
from awx.main.fields import JSONBlob
from awx.main.models.base import CreatedModifiedModel, prevent_search from awx.main.models.base import CreatedModifiedModel, prevent_search
from awx.main.utils import encrypt_field from awx.main.utils import encrypt_field
from awx.conf import settings_registry from awx.conf import settings_registry
@@ -18,7 +19,7 @@ __all__ = ['Setting']
class Setting(CreatedModifiedModel): class Setting(CreatedModifiedModel):
key = models.CharField(max_length=255) key = models.CharField(max_length=255)
value = models.JSONField(null=True) value = JSONBlob(null=True)
user = prevent_search(models.ForeignKey('auth.User', related_name='settings', default=None, null=True, editable=False, on_delete=models.CASCADE)) user = prevent_search(models.ForeignKey('auth.User', related_name='settings', default=None, null=True, editable=False, on_delete=models.CASCADE))
def __str__(self): def __str__(self):

View File

@@ -43,7 +43,6 @@ from rest_framework import serializers
from awx.main.utils.filters import SmartFilter from awx.main.utils.filters import SmartFilter
from awx.main.utils.encryption import encrypt_value, decrypt_value, get_encryption_key from awx.main.utils.encryption import encrypt_value, decrypt_value, get_encryption_key
from awx.main.validators import validate_ssh_private_key from awx.main.validators import validate_ssh_private_key
from awx.main.models.rbac import batch_role_ancestor_rebuilding, Role, ROLE_SINGLETON_SYSTEM_ADMINISTRATOR, ROLE_SINGLETON_SYSTEM_AUDITOR
from awx.main.constants import ENV_BLOCKLIST from awx.main.constants import ENV_BLOCKLIST
from awx.main import utils from awx.main import utils
@@ -130,6 +129,9 @@ def is_implicit_parent(parent_role, child_role):
the model definition. This does not include any role parents that the model definition. This does not include any role parents that
might have been set by the user. might have been set by the user.
""" """
# Avoid circular import
from awx.main.models.rbac import ROLE_SINGLETON_SYSTEM_ADMINISTRATOR, ROLE_SINGLETON_SYSTEM_AUDITOR
if child_role.content_object is None: if child_role.content_object is None:
# The only singleton implicit parent is the system admin being # The only singleton implicit parent is the system admin being
# a parent of the system auditor role # a parent of the system auditor role
@@ -286,6 +288,9 @@ class ImplicitRoleField(models.ForeignKey):
Model = utils.get_current_apps().get_model('main', instance.__class__.__name__) Model = utils.get_current_apps().get_model('main', instance.__class__.__name__)
latest_instance = Model.objects.get(pk=instance.pk) latest_instance = Model.objects.get(pk=instance.pk)
# Avoid circular import
from awx.main.models.rbac import batch_role_ancestor_rebuilding, Role
with batch_role_ancestor_rebuilding(): with batch_role_ancestor_rebuilding():
# Create any missing role objects # Create any missing role objects
missing_roles = [] missing_roles = []
@@ -340,6 +345,10 @@ class ImplicitRoleField(models.ForeignKey):
Role_ = utils.get_current_apps().get_model('main', 'Role') Role_ = utils.get_current_apps().get_model('main', 'Role')
child_ids = [x for x in Role_.parents.through.objects.filter(to_role_id__in=role_ids).distinct().values_list('from_role_id', flat=True)] child_ids = [x for x in Role_.parents.through.objects.filter(to_role_id__in=role_ids).distinct().values_list('from_role_id', flat=True)]
Role_.objects.filter(id__in=role_ids).delete() Role_.objects.filter(id__in=role_ids).delete()
# Avoid circular import
from awx.main.models.rbac import Role
Role.rebuild_role_ancestor_list([], child_ids) Role.rebuild_role_ancestor_list([], child_ids)

View File

@@ -622,7 +622,7 @@ class Migration(migrations.Migration):
('dtend', models.DateTimeField(default=None, null=True, editable=False)), ('dtend', models.DateTimeField(default=None, null=True, editable=False)),
('rrule', models.CharField(max_length=255)), ('rrule', models.CharField(max_length=255)),
('next_run', models.DateTimeField(default=None, null=True, editable=False)), ('next_run', models.DateTimeField(default=None, null=True, editable=False)),
('extra_data', models.JSONField(default=dict, null=True, blank=True)), ('extra_data', awx.main.fields.JSONBlob(default=dict, blank=True)),
( (
'created_by', 'created_by',
models.ForeignKey( models.ForeignKey(
@@ -750,7 +750,7 @@ class Migration(migrations.Migration):
('elapsed', models.DecimalField(editable=False, max_digits=12, decimal_places=3)), ('elapsed', models.DecimalField(editable=False, max_digits=12, decimal_places=3)),
('job_args', models.TextField(default='', editable=False, blank=True)), ('job_args', models.TextField(default='', editable=False, blank=True)),
('job_cwd', models.CharField(default='', max_length=1024, editable=False, blank=True)), ('job_cwd', models.CharField(default='', max_length=1024, editable=False, blank=True)),
('job_env', models.JSONField(default=dict, editable=False, null=True, blank=True)), ('job_env', awx.main.fields.JSONBlob(default=dict, editable=False, blank=True)),
('job_explanation', models.TextField(default='', editable=False, blank=True)), ('job_explanation', models.TextField(default='', editable=False, blank=True)),
('start_args', models.TextField(default='', editable=False, blank=True)), ('start_args', models.TextField(default='', editable=False, blank=True)),
('result_stdout_text', models.TextField(default='', editable=False, blank=True)), ('result_stdout_text', models.TextField(default='', editable=False, blank=True)),
@@ -1034,7 +1034,7 @@ class Migration(migrations.Migration):
('host_config_key', models.CharField(default='', max_length=1024, blank=True)), ('host_config_key', models.CharField(default='', max_length=1024, blank=True)),
('ask_variables_on_launch', models.BooleanField(default=False)), ('ask_variables_on_launch', models.BooleanField(default=False)),
('survey_enabled', models.BooleanField(default=False)), ('survey_enabled', models.BooleanField(default=False)),
('survey_spec', models.JSONField(default=dict, blank=True)), ('survey_spec', awx.main.fields.JSONBlob(default=dict, blank=True)),
], ],
options={ options={
'ordering': ('name',), 'ordering': ('name',),

View File

@@ -198,7 +198,7 @@ class Migration(migrations.Migration):
), ),
('recipients', models.TextField(default='', editable=False, blank=True)), ('recipients', models.TextField(default='', editable=False, blank=True)),
('subject', models.TextField(default='', editable=False, blank=True)), ('subject', models.TextField(default='', editable=False, blank=True)),
('body', models.JSONField(default=dict, null=True, blank=True)), ('body', awx.main.fields.JSONBlob(default=dict, blank=True)),
], ],
options={ options={
'ordering': ('pk',), 'ordering': ('pk',),
@@ -229,7 +229,7 @@ class Migration(migrations.Migration):
], ],
), ),
), ),
('notification_configuration', models.JSONField(default=dict)), ('notification_configuration', awx.main.fields.JSONBlob(default=dict)),
( (
'created_by', 'created_by',
models.ForeignKey( models.ForeignKey(

View File

@@ -220,7 +220,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='char_prompts', name='char_prompts',
field=models.JSONField(default=dict, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
@@ -259,7 +259,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
name='char_prompts', name='char_prompts',
field=models.JSONField(default=dict, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
@@ -307,12 +307,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='job', model_name='job',
name='artifacts', name='artifacts',
field=models.JSONField(default=dict, editable=False, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, editable=False, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='ancestor_artifacts', name='ancestor_artifacts',
field=models.JSONField(default=dict, editable=False, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, editable=False, blank=True),
), ),
# Job timeout settings # Job timeout settings
migrations.AddField( migrations.AddField(
@@ -380,7 +380,9 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='project', model_name='project',
name='playbook_files', name='playbook_files',
field=models.JSONField(default=list, help_text='List of playbooks found in the project', verbose_name='Playbook Files', editable=False, blank=True), field=awx.main.fields.JSONBlob(
default=list, help_text='List of playbooks found in the project', verbose_name='Playbook Files', editable=False, blank=True
),
), ),
# Job events to stdout # Job events to stdout
migrations.AddField( migrations.AddField(
@@ -536,7 +538,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjob', model_name='workflowjob',
name='survey_passwords', name='survey_passwords',
field=models.JSONField(default=dict, editable=False, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, editable=False, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplate', model_name='workflowjobtemplate',
@@ -546,7 +548,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplate', model_name='workflowjobtemplate',
name='survey_spec', name='survey_spec',
field=models.JSONField(default=dict, blank=True), field=awx.main.fields.JSONBlob(default=dict, blank=True),
), ),
# JSON field changes # JSON field changes
migrations.AlterField( migrations.AlterField(
@@ -557,12 +559,12 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='job', model_name='job',
name='artifacts', name='artifacts',
field=models.JSONField(default=dict, editable=False, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, editable=False, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='job', model_name='job',
name='survey_passwords', name='survey_passwords',
field=models.JSONField(default=dict, editable=False, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, editable=False, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='jobevent', model_name='jobevent',
@@ -572,57 +574,59 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='jobtemplate', model_name='jobtemplate',
name='survey_spec', name='survey_spec',
field=models.JSONField(default=dict, blank=True), field=awx.main.fields.JSONBlob(default=dict, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='notification', model_name='notification',
name='body', name='body',
field=models.JSONField(default=dict, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='notificationtemplate', model_name='notificationtemplate',
name='notification_configuration', name='notification_configuration',
field=models.JSONField(default=dict), field=awx.main.fields.JSONBlob(default=dict),
), ),
migrations.AlterField( migrations.AlterField(
model_name='project', model_name='project',
name='playbook_files', name='playbook_files',
field=models.JSONField(default=list, help_text='List of playbooks found in the project', verbose_name='Playbook Files', editable=False, blank=True), field=awx.main.fields.JSONBlob(
default=list, help_text='List of playbooks found in the project', verbose_name='Playbook Files', editable=False, blank=True
),
), ),
migrations.AlterField( migrations.AlterField(
model_name='schedule', model_name='schedule',
name='extra_data', name='extra_data',
field=models.JSONField(default=dict, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='unifiedjob', model_name='unifiedjob',
name='job_env', name='job_env',
field=models.JSONField(default=dict, editable=False, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, editable=False, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='workflowjob', model_name='workflowjob',
name='survey_passwords', name='survey_passwords',
field=models.JSONField(default=dict, editable=False, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, editable=False, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='ancestor_artifacts', name='ancestor_artifacts',
field=models.JSONField(default=dict, editable=False, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, editable=False, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='char_prompts', name='char_prompts',
field=models.JSONField(default=dict, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='workflowjobtemplate', model_name='workflowjobtemplate',
name='survey_spec', name='survey_spec',
field=models.JSONField(default=dict, blank=True), field=awx.main.fields.JSONBlob(default=dict, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
name='char_prompts', name='char_prompts',
field=models.JSONField(default=dict, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, blank=True),
), ),
# Job Project Update # Job Project Update
migrations.AddField( migrations.AddField(

View File

@@ -175,7 +175,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='project', model_name='project',
name='inventory_files', name='inventory_files',
field=models.JSONField( field=awx.main.fields.JSONBlob(
default=list, default=list,
help_text='Suggested list of content that could be Ansible inventory in the project', help_text='Suggested list of content that could be Ansible inventory in the project',
verbose_name='Inventory Files', verbose_name='Inventory Files',

View File

@@ -1,7 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations
import awx.main.fields
class Migration(migrations.Migration): class Migration(migrations.Migration):
@@ -14,6 +16,6 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='activitystream', model_name='activitystream',
name='setting', name='setting',
field=models.JSONField(default=dict, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, blank=True),
), ),
] ]

View File

@@ -20,7 +20,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='schedule', model_name='schedule',
name='char_prompts', name='char_prompts',
field=models.JSONField(default=dict, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='schedule', model_name='schedule',
@@ -37,7 +37,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='schedule', model_name='schedule',
name='survey_passwords', name='survey_passwords',
field=models.JSONField(default=dict, editable=False, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, editable=False, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
@@ -47,12 +47,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='extra_data', name='extra_data',
field=models.JSONField(default=dict, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='survey_passwords', name='survey_passwords',
field=models.JSONField(default=dict, editable=False, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, editable=False, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
@@ -62,12 +62,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
name='extra_data', name='extra_data',
field=models.JSONField(default=dict, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
name='survey_passwords', name='survey_passwords',
field=models.JSONField(default=dict, editable=False, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, editable=False, blank=True),
), ),
# Run data migration before removing the old credential field # Run data migration before removing the old credential field
migrations.RunPython(migration_utils.set_current_apps_for_migrations, migrations.RunPython.noop), migrations.RunPython(migration_utils.set_current_apps_for_migrations, migrations.RunPython.noop),
@@ -85,9 +85,9 @@ class Migration(migrations.Migration):
name='JobLaunchConfig', name='JobLaunchConfig',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('extra_data', models.JSONField(blank=True, null=True, default=dict)), ('extra_data', awx.main.fields.JSONBlob(blank=True, default=dict)),
('survey_passwords', models.JSONField(blank=True, null=True, default=dict, editable=False)), ('survey_passwords', awx.main.fields.JSONBlob(blank=True, default=dict, editable=False)),
('char_prompts', models.JSONField(blank=True, null=True, default=dict)), ('char_prompts', awx.main.fields.JSONBlob(blank=True, default=dict)),
('credentials', models.ManyToManyField(related_name='joblaunchconfigs', to='main.Credential')), ('credentials', models.ManyToManyField(related_name='joblaunchconfigs', to='main.Credential')),
( (
'inventory', 'inventory',

View File

@@ -1,8 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from decimal import Decimal
from django.db import migrations, models from django.db import migrations, models
from decimal import Decimal
import awx.main.fields
class Migration(migrations.Migration): class Migration(migrations.Migration):
@@ -15,7 +17,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='instancegroup', model_name='instancegroup',
name='policy_instance_list', name='policy_instance_list',
field=models.JSONField( field=awx.main.fields.JSONBlob(
default=list, help_text='List of exact-match Instances that will always be automatically assigned to this group', blank=True default=list, help_text='List of exact-match Instances that will always be automatically assigned to this group', blank=True
), ),
), ),

View File

@@ -2,7 +2,9 @@
# Generated by Django 1.11.11 on 2018-05-21 19:51 # Generated by Django 1.11.11 on 2018-05-21 19:51
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models, migrations from django.db import migrations
import awx.main.fields
class Migration(migrations.Migration): class Migration(migrations.Migration):
@@ -15,6 +17,6 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='activitystream', model_name='activitystream',
name='deleted_actor', name='deleted_actor',
field=models.JSONField(null=True), field=awx.main.fields.JSONBlob(null=True),
), ),
] ]

View File

@@ -2,10 +2,11 @@
# Generated by Django 1.11.11 on 2018-09-27 19:50 # Generated by Django 1.11.11 on 2018-09-27 19:50
from __future__ import unicode_literals from __future__ import unicode_literals
import awx.main.fields
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import awx.main.fields
class Migration(migrations.Migration): class Migration(migrations.Migration):
@@ -17,7 +18,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjob', model_name='workflowjob',
name='char_prompts', name='char_prompts',
field=models.JSONField(blank=True, null=True, default=dict), field=awx.main.fields.JSONBlob(blank=True, default=dict),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjob', model_name='workflowjob',

View File

@@ -4,6 +4,7 @@ from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import awx.main.fields
import awx.main.models.notifications import awx.main.models.notifications
@@ -17,7 +18,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='notificationtemplate', model_name='notificationtemplate',
name='messages', name='messages',
field=models.JSONField( field=awx.main.fields.JSONBlob(
default=awx.main.models.notifications.NotificationTemplate.default_messages, default=awx.main.models.notifications.NotificationTemplate.default_messages,
help_text='Optional custom messages for notification template.', help_text='Optional custom messages for notification template.',
null=True, null=True,

View File

@@ -24,7 +24,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplate', model_name='workflowjobtemplate',
name='char_prompts', name='char_prompts',
field=models.JSONField(blank=True, null=True, default=dict), field=awx.main.fields.JSONBlob(blank=True, default=dict),
), ),
migrations.AlterField( migrations.AlterField(
model_name='joblaunchconfig', model_name='joblaunchconfig',

View File

@@ -29,7 +29,7 @@ SQUASHED_30 = {
migrations.AddField( migrations.AddField(
model_name='job', model_name='job',
name='survey_passwords', name='survey_passwords',
field=models.JSONField(default=dict, editable=False, null=True, blank=True), field=awx.main.fields.JSONBlob(default=dict, editable=False, blank=True),
), ),
], ],
'0031_v302_migrate_survey_passwords': [ '0031_v302_migrate_survey_passwords': [

View File

@@ -3,6 +3,7 @@
# AWX # AWX
from awx.api.versioning import reverse from awx.api.versioning import reverse
from awx.main.fields import JSONBlob
from awx.main.models.base import accepts_json from awx.main.models.base import accepts_json
# Django # Django
@@ -35,7 +36,7 @@ class ActivityStream(models.Model):
operation = models.CharField(max_length=13, choices=OPERATION_CHOICES) operation = models.CharField(max_length=13, choices=OPERATION_CHOICES)
timestamp = models.DateTimeField(auto_now_add=True) timestamp = models.DateTimeField(auto_now_add=True)
changes = accepts_json(models.TextField(blank=True)) changes = accepts_json(models.TextField(blank=True))
deleted_actor = models.JSONField(null=True) deleted_actor = JSONBlob(null=True)
action_node = models.CharField( action_node = models.CharField(
blank=True, blank=True,
default='', default='',
@@ -83,7 +84,7 @@ class ActivityStream(models.Model):
o_auth2_application = models.ManyToManyField("OAuth2Application", blank=True) o_auth2_application = models.ManyToManyField("OAuth2Application", blank=True)
o_auth2_access_token = models.ManyToManyField("OAuth2AccessToken", blank=True) o_auth2_access_token = models.ManyToManyField("OAuth2AccessToken", blank=True)
setting = models.JSONField(default=dict, null=True, blank=True) setting = JSONBlob(default=dict, blank=True)
def __str__(self): def __str__(self):
operation = self.operation if 'operation' in self.__dict__ else '_delayed_' operation = self.operation if 'operation' in self.__dict__ else '_delayed_'

View File

@@ -18,6 +18,7 @@ from solo.models import SingletonModel
from awx import __version__ as awx_application_version from awx import __version__ as awx_application_version
from awx.api.versioning import reverse from awx.api.versioning import reverse
from awx.main.fields import JSONBlob
from awx.main.managers import InstanceManager, InstanceGroupManager, UUID_DEFAULT from awx.main.managers import InstanceManager, InstanceGroupManager, UUID_DEFAULT
from awx.main.constants import JOB_FOLDER_PREFIX from awx.main.constants import JOB_FOLDER_PREFIX
from awx.main.models.base import BaseModel, HasEditsMixin, prevent_search from awx.main.models.base import BaseModel, HasEditsMixin, prevent_search
@@ -327,7 +328,7 @@ class InstanceGroup(HasPolicyEditsMixin, BaseModel, RelatedJobsMixin):
) )
policy_instance_percentage = models.IntegerField(default=0, help_text=_("Percentage of Instances to automatically assign to this group")) policy_instance_percentage = models.IntegerField(default=0, help_text=_("Percentage of Instances to automatically assign to this group"))
policy_instance_minimum = models.IntegerField(default=0, help_text=_("Static minimum number of Instances to automatically assign to this group")) policy_instance_minimum = models.IntegerField(default=0, help_text=_("Static minimum number of Instances to automatically assign to this group"))
policy_instance_list = models.JSONField( policy_instance_list = JSONBlob(
default=list, blank=True, help_text=_("List of exact-match Instances that will always be automatically assigned to this group") default=list, blank=True, help_text=_("List of exact-match Instances that will always be automatically assigned to this group")
) )

View File

@@ -44,7 +44,7 @@ from awx.main.models.notifications import (
JobNotificationMixin, JobNotificationMixin,
) )
from awx.main.utils import parse_yaml_or_json, getattr_dne, NullablePromptPseudoField from awx.main.utils import parse_yaml_or_json, getattr_dne, NullablePromptPseudoField
from awx.main.fields import ImplicitRoleField, AskForField from awx.main.fields import ImplicitRoleField, AskForField, JSONBlob
from awx.main.models.mixins import ( from awx.main.models.mixins import (
ResourceMixin, ResourceMixin,
SurveyJobTemplateMixin, SurveyJobTemplateMixin,
@@ -546,9 +546,8 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana
editable=False, editable=False,
through='JobHostSummary', through='JobHostSummary',
) )
artifacts = models.JSONField( artifacts = JSONBlob(
default=dict, default=dict,
null=True,
blank=True, blank=True,
editable=False, editable=False,
) )
@@ -886,7 +885,7 @@ class LaunchTimeConfigBase(BaseModel):
) )
# All standard fields are stored in this dictionary field # All standard fields are stored in this dictionary field
# This is a solution to the nullable CharField problem, specific to prompting # This is a solution to the nullable CharField problem, specific to prompting
char_prompts = models.JSONField(default=dict, null=True, blank=True) char_prompts = JSONBlob(default=dict, blank=True)
def prompts_dict(self, display=False): def prompts_dict(self, display=False):
data = {} data = {}
@@ -939,12 +938,11 @@ class LaunchTimeConfig(LaunchTimeConfigBase):
abstract = True abstract = True
# Special case prompting fields, even more special than the other ones # Special case prompting fields, even more special than the other ones
extra_data = models.JSONField(default=dict, null=True, blank=True) extra_data = JSONBlob(default=dict, blank=True)
survey_passwords = prevent_search( survey_passwords = prevent_search(
models.JSONField( JSONBlob(
default=dict, default=dict,
editable=False, editable=False,
null=True,
blank=True, blank=True,
) )
) )

View File

@@ -24,7 +24,7 @@ from awx.main.utils import parse_yaml_or_json, get_custom_venv_choices, get_lice
from awx.main.utils.execution_environments import get_default_execution_environment from awx.main.utils.execution_environments import get_default_execution_environment
from awx.main.utils.encryption import decrypt_value, get_encryption_key, is_encrypted from awx.main.utils.encryption import decrypt_value, get_encryption_key, is_encrypted
from awx.main.utils.polymorphic import build_polymorphic_ctypes_map from awx.main.utils.polymorphic import build_polymorphic_ctypes_map
from awx.main.fields import AskForField from awx.main.fields import AskForField, JSONBlob
from awx.main.constants import ACTIVE_STATES from awx.main.constants import ACTIVE_STATES
@@ -103,7 +103,7 @@ class SurveyJobTemplateMixin(models.Model):
survey_enabled = models.BooleanField( survey_enabled = models.BooleanField(
default=False, default=False,
) )
survey_spec = prevent_search(models.JSONField(default=dict, blank=True)) survey_spec = prevent_search(JSONBlob(default=dict, blank=True))
ask_variables_on_launch = AskForField(blank=True, default=False, allows_field='extra_vars') ask_variables_on_launch = AskForField(blank=True, default=False, allows_field='extra_vars')
def survey_password_variables(self): def survey_password_variables(self):
@@ -365,10 +365,9 @@ class SurveyJobMixin(models.Model):
abstract = True abstract = True
survey_passwords = prevent_search( survey_passwords = prevent_search(
models.JSONField( JSONBlob(
default=dict, default=dict,
editable=False, editable=False,
null=True,
blank=True, blank=True,
) )
) )

View File

@@ -17,6 +17,7 @@ from jinja2.exceptions import TemplateSyntaxError, UndefinedError, SecurityError
# AWX # AWX
from awx.api.versioning import reverse from awx.api.versioning import reverse
from awx.main.fields import JSONBlob
from awx.main.models.base import CommonModelNameNotUnique, CreatedModifiedModel, prevent_search from awx.main.models.base import CommonModelNameNotUnique, CreatedModifiedModel, prevent_search
from awx.main.utils import encrypt_field, decrypt_field, set_environ from awx.main.utils import encrypt_field, decrypt_field, set_environ
from awx.main.notifications.email_backend import CustomEmailBackend from awx.main.notifications.email_backend import CustomEmailBackend
@@ -69,12 +70,12 @@ class NotificationTemplate(CommonModelNameNotUnique):
choices=NOTIFICATION_TYPE_CHOICES, choices=NOTIFICATION_TYPE_CHOICES,
) )
notification_configuration = prevent_search(models.JSONField(default=dict)) notification_configuration = prevent_search(JSONBlob(default=dict))
def default_messages(): def default_messages():
return {'started': None, 'success': None, 'error': None, 'workflow_approval': None} return {'started': None, 'success': None, 'error': None, 'workflow_approval': None}
messages = models.JSONField(null=True, blank=True, default=default_messages, help_text=_('Optional custom messages for notification template.')) messages = JSONBlob(null=True, blank=True, default=default_messages, help_text=_('Optional custom messages for notification template.'))
def has_message(self, condition): def has_message(self, condition):
potential_template = self.messages.get(condition, {}) potential_template = self.messages.get(condition, {})
@@ -236,7 +237,7 @@ class Notification(CreatedModifiedModel):
default='', default='',
editable=False, editable=False,
) )
body = models.JSONField(default=dict, null=True, blank=True) body = JSONBlob(default=dict, blank=True)
def get_absolute_url(self, request=None): def get_absolute_url(self, request=None):
return reverse('api:notification_detail', kwargs={'pk': self.pk}, request=request) return reverse('api:notification_detail', kwargs={'pk': self.pk}, request=request)

View File

@@ -33,7 +33,7 @@ from awx.main.models.mixins import ResourceMixin, TaskManagerProjectUpdateMixin,
from awx.main.utils import update_scm_url, polymorphic from awx.main.utils import update_scm_url, polymorphic
from awx.main.utils.ansible import skip_directory, could_be_inventory, could_be_playbook from awx.main.utils.ansible import skip_directory, could_be_inventory, could_be_playbook
from awx.main.utils.execution_environments import get_control_plane_execution_environment from awx.main.utils.execution_environments import get_control_plane_execution_environment
from awx.main.fields import ImplicitRoleField from awx.main.fields import ImplicitRoleField, JSONBlob
from awx.main.models.rbac import ( from awx.main.models.rbac import (
ROLE_SINGLETON_SYSTEM_ADMINISTRATOR, ROLE_SINGLETON_SYSTEM_ADMINISTRATOR,
ROLE_SINGLETON_SYSTEM_AUDITOR, ROLE_SINGLETON_SYSTEM_AUDITOR,
@@ -293,7 +293,7 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin, CustomVirtualEn
help_text=_('The last revision fetched by a project update'), help_text=_('The last revision fetched by a project update'),
) )
playbook_files = models.JSONField( playbook_files = JSONBlob(
default=list, default=list,
blank=True, blank=True,
editable=False, editable=False,
@@ -301,7 +301,7 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin, CustomVirtualEn
help_text=_('List of playbooks found in the project'), help_text=_('List of playbooks found in the project'),
) )
inventory_files = models.JSONField( inventory_files = JSONBlob(
default=list, default=list,
blank=True, blank=True,
editable=False, editable=False,

View File

@@ -54,7 +54,7 @@ from awx.main.utils import polymorphic
from awx.main.constants import ACTIVE_STATES, CAN_CANCEL from awx.main.constants import ACTIVE_STATES, CAN_CANCEL
from awx.main.redact import UriCleaner, REPLACE_STR from awx.main.redact import UriCleaner, REPLACE_STR
from awx.main.consumers import emit_channel_notification from awx.main.consumers import emit_channel_notification
from awx.main.fields import AskForField, OrderedManyToManyField from awx.main.fields import AskForField, OrderedManyToManyField, JSONBlob
__all__ = ['UnifiedJobTemplate', 'UnifiedJob', 'StdoutMaxBytesExceeded'] __all__ = ['UnifiedJobTemplate', 'UnifiedJob', 'StdoutMaxBytesExceeded']
@@ -653,9 +653,8 @@ class UnifiedJob(
editable=False, editable=False,
) )
job_env = prevent_search( job_env = prevent_search(
models.JSONField( JSONBlob(
default=dict, default=dict,
null=True,
blank=True, blank=True,
editable=False, editable=False,
) )

View File

@@ -28,7 +28,7 @@ from awx.main.models import prevent_search, accepts_json, UnifiedJobTemplate, Un
from awx.main.models.notifications import NotificationTemplate, JobNotificationMixin from awx.main.models.notifications import NotificationTemplate, JobNotificationMixin
from awx.main.models.base import CreatedModifiedModel, VarsDictProperty from awx.main.models.base import CreatedModifiedModel, VarsDictProperty
from awx.main.models.rbac import ROLE_SINGLETON_SYSTEM_ADMINISTRATOR, ROLE_SINGLETON_SYSTEM_AUDITOR from awx.main.models.rbac import ROLE_SINGLETON_SYSTEM_ADMINISTRATOR, ROLE_SINGLETON_SYSTEM_AUDITOR
from awx.main.fields import ImplicitRoleField, AskForField from awx.main.fields import ImplicitRoleField, AskForField, JSONBlob
from awx.main.models.mixins import ( from awx.main.models.mixins import (
ResourceMixin, ResourceMixin,
SurveyJobTemplateMixin, SurveyJobTemplateMixin,
@@ -231,9 +231,8 @@ class WorkflowJobNode(WorkflowNodeBase):
default=None, default=None,
on_delete=models.CASCADE, on_delete=models.CASCADE,
) )
ancestor_artifacts = models.JSONField( ancestor_artifacts = JSONBlob(
default=dict, default=dict,
null=True,
blank=True, blank=True,
editable=False, editable=False,
) )