summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsoftwarefactory-project-zuul[bot] <33884098+softwarefactory-project-zuul[bot]@users.noreply.github.com>2019-07-16 00:56:22 +0200
committerGitHub <noreply@github.com>2019-07-16 00:56:22 +0200
commitb8b98b136b26e038e277e4333c62fe0d34a36ad6 (patch)
treec5de9b3312910ce85c3a29a45e993749a233cf90
parentMerge pull request #4323 from keithjgrant/4248-tooltip-permissions-list (diff)
parentEnsure that the Postgres client is installed (diff)
downloadawx-b8b98b136b26e038e277e4333c62fe0d34a36ad6.tar.xz
awx-b8b98b136b26e038e277e4333c62fe0d34a36ad6.zip
Merge pull request #4070 from jbradberry/upgrade-django-2.2
Upgrade Django to 2.2 Reviewed-by: https://github.com/softwarefactory-project-zuul[bot]
-rw-r--r--awx/__init__.py44
-rw-r--r--awx/api/generics.py18
-rw-r--r--awx/api/permissions.py2
-rw-r--r--awx/api/serializers.py36
-rw-r--r--awx/api/views/__init__.py2
-rw-r--r--awx/conf/migrations/0001_initial.py3
-rw-r--r--awx/main/consumers.py2
-rw-r--r--awx/main/fields.py68
-rw-r--r--awx/main/middleware.py2
-rw-r--r--awx/main/migrations/0001_initial.py72
-rw-r--r--awx/main/migrations/0002_squashed_v300_release.py29
-rw-r--r--awx/main/migrations/0003_squashed_v300_v303_updates.py2
-rw-r--r--awx/main/migrations/0004_squashed_v310_release.py44
-rw-r--r--awx/main/migrations/0006_v320_release.py42
-rw-r--r--awx/main/migrations/0008_v320_drop_v1_credential_fields.py4
-rw-r--r--awx/main/migrations/0014_v330_saved_launchtime_configs.py38
-rw-r--r--awx/main/migrations/0018_v330_add_additional_stdout_events.py6
-rw-r--r--awx/main/migrations/0053_v340_workflow_inventory.py4
-rw-r--r--awx/main/migrations/0067_v350_credential_plugins.py2
-rw-r--r--awx/main/migrations/_squashed_30.py2
-rw-r--r--awx/main/models/__init__.py20
-rw-r--r--awx/main/models/credential/__init__.py8
-rw-r--r--awx/main/models/events.py4
-rw-r--r--awx/main/models/inventory.py2
-rw-r--r--awx/main/models/jobs.py8
-rw-r--r--awx/main/models/mixins.py4
-rw-r--r--awx/main/models/unified_jobs.py2
-rw-r--r--awx/main/models/workflow.py2
-rw-r--r--awx/main/tests/functional/api/test_instance_group.py2
-rw-r--r--awx/main/tests/functional/commands/test_commands.py1
-rw-r--r--awx/main/tests/functional/conftest.py4
-rw-r--r--awx/main/tests/functional/models/test_schedule.py2
-rw-r--r--awx/main/tests/functional/models/test_workflow.py1
-rw-r--r--awx/main/tests/functional/task_management/test_capacity.py3
-rw-r--r--awx/main/tests/functional/test_jobs.py2
-rw-r--r--awx/main/tests/functional/test_rbac_user.py1
-rw-r--r--awx/main/tests/unit/api/test_generics.py9
-rw-r--r--awx/main/tests/unit/conftest.py12
-rw-r--r--awx/main/tests/unit/models/test_events.py2
-rw-r--r--awx/main/utils/named_url_graph.py4
-rw-r--r--awx/main/views.py6
-rw-r--r--awx/sso/backends.py27
-rw-r--r--awx/sso/middleware.py2
-rw-r--r--awx/sso/migrations/0001_initial.py2
-rw-r--r--awx/sso/pipeline.py10
-rw-r--r--awx/sso/tests/conftest.py1
-rw-r--r--awx/sso/tests/functional/test_get_or_set_enterprise_user.py7
-rw-r--r--awx/sso/tests/unit/test_tacacsplus.py8
-rw-r--r--awx/sso/views.py2
-rw-r--r--awx/templates/error.html2
-rw-r--r--awx/templates/rest_framework/login.html2
-rw-r--r--awx/wsgi.py7
-rw-r--r--docs/licenses/django-jsonbfield.txt28
-rw-r--r--docs/licenses/sqlparse.txt25
-rw-r--r--requirements/requirements.in4
-rw-r--r--requirements/requirements.txt5
-rw-r--r--requirements/requirements_git.txt1
-rw-r--r--tools/docker-compose/Dockerfile15
58 files changed, 293 insertions, 376 deletions
diff --git a/awx/__init__.py b/awx/__init__.py
index 0963e7fc91..8583d5bbf6 100644
--- a/awx/__init__.py
+++ b/awx/__init__.py
@@ -25,9 +25,8 @@ import hashlib
try:
import django
- from django.utils.encoding import force_bytes
- from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from django.db.backends.base import schema
+ from django.db.backends.utils import names_digest
HAS_DJANGO = True
except ImportError:
HAS_DJANGO = False
@@ -37,30 +36,33 @@ if HAS_DJANGO is True:
# This line exists to make sure we don't regress on FIPS support if we
# upgrade Django; if you're upgrading Django and see this error,
# update the version check below, and confirm that FIPS still works.
- if django.__version__ != '1.11.20':
- raise RuntimeError("Django version other than 1.11.20 detected {}. \
- Subclassing BaseDatabaseSchemaEditor is known to work for Django 1.11.20 \
- and may not work in newer Django versions.".format(django.__version__))
+ # If operating in a FIPS environment, `hashlib.md5()` will raise a `ValueError`,
+ # but will support the `usedforsecurity` keyword on RHEL and Centos systems.
+
+ # Keep an eye on https://code.djangoproject.com/ticket/28401
+ target_version = '2.2.2'
+ if django.__version__ != target_version:
+ raise RuntimeError(
+ "Django version other than {target} detected: {current}. "
+ "Overriding `names_digest` is known to work for Django {target} "
+ "and may not work in other Django versions.".format(target=target_version,
+ current=django.__version__)
+ )
-
- class FipsBaseDatabaseSchemaEditor(BaseDatabaseSchemaEditor):
-
- @classmethod
- def _digest(cls, *args):
+ try:
+ names_digest('foo', 'bar', 'baz', length=8)
+ except ValueError:
+ def names_digest(*args, length):
"""
- Generates a 32-bit digest of a set of arguments that can be used to
- shorten identifying names.
+ Generate a 32-bit digest of a set of arguments that can be used to shorten
+ identifying names. Support for use in FIPS environments.
"""
- try:
- h = hashlib.md5()
- except ValueError:
- h = hashlib.md5(usedforsecurity=False)
+ h = hashlib.md5(usedforsecurity=False)
for arg in args:
- h.update(force_bytes(arg))
- return h.hexdigest()[:8]
-
+ h.update(arg.encode())
+ return h.hexdigest()[:length]
- schema.BaseDatabaseSchemaEditor = FipsBaseDatabaseSchemaEditor
+ schema.names_digest = names_digest
def find_commands(management_dir):
diff --git a/awx/api/generics.py b/awx/api/generics.py
index 7c17799d11..c66c9b7348 100644
--- a/awx/api/generics.py
+++ b/awx/api/generics.py
@@ -401,21 +401,21 @@ class ListAPIView(generics.ListAPIView, GenericAPIView):
continue
if getattr(field, 'related_model', None):
fields.add('{}__search'.format(field.name))
- for rel in self.model._meta.related_objects:
- name = rel.related_name
- if isinstance(rel, OneToOneRel) and self.model._meta.verbose_name.startswith('unified'):
+ for related in self.model._meta.related_objects:
+ name = related.related_name
+ if isinstance(related, OneToOneRel) and self.model._meta.verbose_name.startswith('unified'):
# Add underscores for polymorphic subclasses for user utility
- name = rel.related_model._meta.verbose_name.replace(" ", "_")
+ name = related.related_model._meta.verbose_name.replace(" ", "_")
if skip_related_name(name) or name.endswith('+'):
continue
fields.add('{}__search'.format(name))
- m2m_rel = []
- m2m_rel += self.model._meta.local_many_to_many
+ m2m_related = []
+ m2m_related += self.model._meta.local_many_to_many
if issubclass(self.model, UnifiedJobTemplate) and self.model != UnifiedJobTemplate:
- m2m_rel += UnifiedJobTemplate._meta.local_many_to_many
+ m2m_related += UnifiedJobTemplate._meta.local_many_to_many
if issubclass(self.model, UnifiedJob) and self.model != UnifiedJob:
- m2m_rel += UnifiedJob._meta.local_many_to_many
- for relationship in m2m_rel:
+ m2m_related += UnifiedJob._meta.local_many_to_many
+ for relationship in m2m_related:
if skip_related_name(relationship.name):
continue
if relationship.related_model._meta.app_label != 'main':
diff --git a/awx/api/permissions.py b/awx/api/permissions.py
index 867388a08d..3c4de0ad27 100644
--- a/awx/api/permissions.py
+++ b/awx/api/permissions.py
@@ -95,7 +95,7 @@ class ModelAccessPermission(permissions.BasePermission):
'''
# Don't allow anonymous users. 401, not 403, hence no raised exception.
- if not request.user or request.user.is_anonymous():
+ if not request.user or request.user.is_anonymous:
return False
# Always allow superusers
diff --git a/awx/api/serializers.py b/awx/api/serializers.py
index 60db5cf9f3..d336587de0 100644
--- a/awx/api/serializers.py
+++ b/awx/api/serializers.py
@@ -2560,7 +2560,7 @@ class CredentialSerializer(BaseSerializer):
def validate_credential_type(self, credential_type):
if self.instance and credential_type.pk != self.instance.credential_type.pk:
- for rel in (
+ for related_objects in (
'ad_hoc_commands',
'insights_inventories',
'unifiedjobs',
@@ -2569,7 +2569,7 @@ class CredentialSerializer(BaseSerializer):
'projectupdates',
'workflowjobnodes'
):
- if getattr(self.instance, rel).count() > 0:
+ if getattr(self.instance, related_objects).count() > 0:
raise ValidationError(
_('You cannot change the credential type of the credential, as it may break the functionality'
' of the resources using it.'),
@@ -4640,37 +4640,37 @@ class ActivityStreamSerializer(BaseSerializer):
return ""
def get_related(self, obj):
- rel = {}
+ data = {}
if obj.actor is not None:
- rel['actor'] = self.reverse('api:user_detail', kwargs={'pk': obj.actor.pk})
+ data['actor'] = self.reverse('api:user_detail', kwargs={'pk': obj.actor.pk})
for fk, __ in self._local_summarizable_fk_fields:
if not hasattr(obj, fk):
continue
- m2m_list = self._get_rel(obj, fk)
+ m2m_list = self._get_related_objects(obj, fk)
if m2m_list:
- rel[fk] = []
+ data[fk] = []
id_list = []
- for thisItem in m2m_list:
- if getattr(thisItem, 'id', None) in id_list:
+ for item in m2m_list:
+ if getattr(item, 'id', None) in id_list:
continue
- id_list.append(getattr(thisItem, 'id', None))
- if hasattr(thisItem, 'get_absolute_url'):
- rel_url = thisItem.get_absolute_url(self.context.get('request'))
+ id_list.append(getattr(item, 'id', None))
+ if hasattr(item, 'get_absolute_url'):
+ url = item.get_absolute_url(self.context.get('request'))
else:
view_name = fk + '_detail'
- rel_url = self.reverse('api:' + view_name, kwargs={'pk': thisItem.id})
- rel[fk].append(rel_url)
+ url = self.reverse('api:' + view_name, kwargs={'pk': item.id})
+ data[fk].append(url)
if fk == 'schedule':
- rel['unified_job_template'] = thisItem.unified_job_template.get_absolute_url(self.context.get('request'))
+ data['unified_job_template'] = item.unified_job_template.get_absolute_url(self.context.get('request'))
if obj.setting and obj.setting.get('category', None):
- rel['setting'] = self.reverse(
+ data['setting'] = self.reverse(
'api:setting_singleton_detail',
kwargs={'category_slug': obj.setting['category']}
)
- return rel
+ return data
- def _get_rel(self, obj, fk):
+ def _get_related_objects(self, obj, fk):
related_model = ActivityStream._meta.get_field(fk).related_model
related_manager = getattr(obj, fk)
if issubclass(related_model, PolymorphicModel) and hasattr(obj, '_prefetched_objects_cache'):
@@ -4703,7 +4703,7 @@ class ActivityStreamSerializer(BaseSerializer):
try:
if not hasattr(obj, fk):
continue
- m2m_list = self._get_rel(obj, fk)
+ m2m_list = self._get_related_objects(obj, fk)
if m2m_list:
summary_fields[fk] = []
for thisItem in m2m_list:
diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py
index 092675267d..1c57c35bd8 100644
--- a/awx/api/views/__init__.py
+++ b/awx/api/views/__init__.py
@@ -2997,7 +2997,7 @@ class WorkflowJobTemplateNodeChildrenBaseList(EnforceParentRelationshipMixin, Su
relationships = ['success_nodes', 'failure_nodes', 'always_nodes']
relationships.remove(self.relationship)
qs = functools.reduce(lambda x, y: (x | y),
- (Q(**{'{}__in'.format(rel): [sub.id]}) for rel in relationships))
+ (Q(**{'{}__in'.format(r): [sub.id]}) for r in relationships))
if models.WorkflowJobTemplateNode.objects.filter(Q(pk=parent.id) & qs).exists():
return {"Error": _("Relationship not allowed.")}
diff --git a/awx/conf/migrations/0001_initial.py b/awx/conf/migrations/0001_initial.py
index f9613b15d1..22330e330c 100644
--- a/awx/conf/migrations/0001_initial.py
+++ b/awx/conf/migrations/0001_initial.py
@@ -21,7 +21,8 @@ class Migration(migrations.Migration):
('modified', models.DateTimeField(default=None, editable=False)),
('key', models.CharField(max_length=255)),
('value', jsonfield.fields.JSONField(null=True)),
- ('user', models.ForeignKey(related_name='settings', default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
+ ('user', models.ForeignKey(related_name='settings', default=None, editable=False,
+ to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True)),
],
options={
'abstract': False,
diff --git a/awx/main/consumers.py b/awx/main/consumers.py
index aee25e8ff9..a4fcdc96a6 100644
--- a/awx/main/consumers.py
+++ b/awx/main/consumers.py
@@ -24,7 +24,7 @@ def ws_connect(message):
headers = dict(message.content.get('headers', ''))
message.reply_channel.send({"accept": True})
message.content['method'] = 'FAKE'
- if message.user.is_authenticated():
+ if message.user.is_authenticated:
message.reply_channel.send(
{"text": json.dumps({"accept": True, "user": message.user.id})}
)
diff --git a/awx/main/fields.py b/awx/main/fields.py
index 70ee365086..110492b173 100644
--- a/awx/main/fields.py
+++ b/awx/main/fields.py
@@ -11,8 +11,9 @@ from jinja2 import Environment, StrictUndefined
from jinja2.exceptions import UndefinedError, TemplateSyntaxError
# Django
-import django
+from django.contrib.postgres.fields import JSONField as upstream_JSONBField
from django.core import exceptions as django_exceptions
+from django.core.serializers.json import DjangoJSONEncoder
from django.db.models.signals import (
post_save,
post_delete,
@@ -37,7 +38,6 @@ import jsonschema.exceptions
# Django-JSONField
from jsonfield import JSONField as upstream_JSONField
-from jsonbfield.fields import JSONField as upstream_JSONBField
# DRF
from rest_framework import serializers
@@ -76,10 +76,10 @@ class JSONField(upstream_JSONField):
def db_type(self, connection):
return 'text'
- def from_db_value(self, value, expression, connection, context):
+ def from_db_value(self, value, expression, connection):
if value in {'', None} and not self.null:
return {}
- return super(JSONField, self).from_db_value(value, expression, connection, context)
+ return super(JSONField, self).from_db_value(value, expression, connection)
class JSONBField(upstream_JSONBField):
@@ -91,12 +91,12 @@ class JSONBField(upstream_JSONBField):
def get_db_prep_value(self, value, connection, prepared=False):
if connection.vendor == 'sqlite':
# sqlite (which we use for tests) does not support jsonb;
- return json.dumps(value)
+ return json.dumps(value, cls=DjangoJSONEncoder)
return super(JSONBField, self).get_db_prep_value(
value, connection, prepared
)
- def from_db_value(self, value, expression, connection, context):
+ def from_db_value(self, value, expression, connection):
# Work around a bug in django-jsonfield
# https://bitbucket.org/schinckel/django-jsonfield/issues/57/cannot-use-in-the-same-project-as-djangos
if isinstance(value, str):
@@ -112,14 +112,9 @@ class AutoSingleRelatedObjectDescriptor(ReverseOneToOneDescriptor):
def __get__(self, instance, instance_type=None):
try:
- return super(AutoSingleRelatedObjectDescriptor,
- self).__get__(instance, instance_type)
+ return super(AutoSingleRelatedObjectDescriptor, self).__get__(instance, instance_type)
except self.related.related_model.DoesNotExist:
obj = self.related.related_model(**{self.related.field.name: instance})
- if self.related.field.rel.parent_link:
- raise NotImplementedError('not supported with polymorphic!')
- for f in instance._meta.local_fields:
- setattr(obj, f.name, getattr(instance, f.name))
obj.save()
return obj
@@ -453,21 +448,6 @@ class JSONSchemaField(JSONBField):
params={'value': value},
)
- def get_db_prep_value(self, value, connection, prepared=False):
- if connection.vendor == 'sqlite':
- # sqlite (which we use for tests) does not support jsonb;
- return json.dumps(value)
- return super(JSONSchemaField, self).get_db_prep_value(
- value, connection, prepared
- )
-
- def from_db_value(self, value, expression, connection, context):
- # Work around a bug in django-jsonfield
- # https://bitbucket.org/schinckel/django-jsonfield/issues/57/cannot-use-in-the-same-project-as-djangos
- if isinstance(value, str):
- return json.loads(value)
- return value
-
@JSONSchemaField.format_checker.checks('vault_id')
def format_vault_id(value):
@@ -986,7 +966,7 @@ class OAuth2ClientSecretField(models.CharField):
encrypt_value(value), connection, prepared
)
- def from_db_value(self, value, expression, connection, context):
+ def from_db_value(self, value, expression, connection):
if value and value.startswith('$encrypted$'):
return decrypt_value(get_encryption_key('value', pk=None), value)
return value
@@ -1022,38 +1002,6 @@ class OrderedManyToManyDescriptor(ManyToManyDescriptor):
'%s__position' % self.through._meta.model_name
)
- def add(self, *objs):
- # Django < 2 doesn't support this method on
- # ManyToManyFields w/ an intermediary model
- # We should be able to remove this code snippet when we
- # upgrade Django.
- # see: https://github.com/django/django/blob/stable/1.11.x/django/db/models/fields/related_descriptors.py#L926
- if not django.__version__.startswith('1.'):
- raise RuntimeError(
- 'This method is no longer necessary in Django>=2'
- )
- try:
- self.through._meta.auto_created = True
- super(OrderedManyRelatedManager, self).add(*objs)
- finally:
- self.through._meta.auto_created = False
-
- def remove(self, *objs):
- # Django < 2 doesn't support this method on
- # ManyToManyFields w/ an intermediary model
- # We should be able to remove this code snippet when we
- # upgrade Django.
- # see: https://github.com/django/django/blob/stable/1.11.x/django/db/models/fields/related_descriptors.py#L944
- if not django.__version__.startswith('1.'):
- raise RuntimeError(
- 'This method is no longer necessary in Django>=2'
- )
- try:
- self.through._meta.auto_created = True
- super(OrderedManyRelatedManager, self).remove(*objs)
- finally:
- self.through._meta.auto_created = False
-
return OrderedManyRelatedManager
return add_custom_queryset_to_many_related_manager(
diff --git a/awx/main/middleware.py b/awx/main/middleware.py
index 86bb58e33e..b1a03c9a38 100644
--- a/awx/main/middleware.py
+++ b/awx/main/middleware.py
@@ -73,7 +73,7 @@ class ActivityStreamMiddleware(threading.local, MiddlewareMixin):
super().__init__(get_response)
def process_request(self, request):
- if hasattr(request, 'user') and hasattr(request.user, 'is_authenticated') and request.user.is_authenticated():
+ if hasattr(request, 'user') and request.user.is_authenticated:
user = request.user
else:
user = None
diff --git a/awx/main/migrations/0001_initial.py b/awx/main/migrations/0001_initial.py
index fb8a88e676..e14624911a 100644
--- a/awx/main/migrations/0001_initial.py
+++ b/awx/main/migrations/0001_initial.py
@@ -44,7 +44,7 @@ class Migration(migrations.Migration):
('modified', models.DateTimeField(default=None, editable=False)),
('host_name', models.CharField(default='', max_length=1024, editable=False)),
('event', models.CharField(max_length=100, choices=[('runner_on_failed', 'Host Failed'), ('runner_on_ok', 'Host OK'), ('runner_on_unreachable', 'Host Unreachable'), ('runner_on_skipped', 'Host Skipped')])),
- ('event_data', jsonfield.fields.JSONField(default={}, blank=True)),
+ ('event_data', jsonfield.fields.JSONField(default=dict, blank=True)),
('failed', models.BooleanField(default=False, editable=False)),
('changed', models.BooleanField(default=False, editable=False)),
('counter', models.PositiveIntegerField(default=0)),
@@ -62,7 +62,7 @@ class Migration(migrations.Migration):
('expires', models.DateTimeField(default=django.utils.timezone.now)),
('request_hash', models.CharField(default='', max_length=40, blank=True)),
('reason', models.CharField(default='', help_text='Reason the auth token was invalidated.', max_length=1024, blank=True)),
- ('user', models.ForeignKey(related_name='auth_tokens', to=settings.AUTH_USER_MODEL)),
+ ('user', models.ForeignKey(related_name='auth_tokens', on_delete=models.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
@@ -198,7 +198,7 @@ class Migration(migrations.Migration):
('created', models.DateTimeField(default=None, editable=False)),
('modified', models.DateTimeField(default=None, editable=False)),
('event', models.CharField(max_length=100, choices=[('runner_on_failed', 'Host Failed'), ('runner_on_ok', 'Host OK'), ('runner_on_error', 'Host Failure'), ('runner_on_skipped', 'Host Skipped'), ('runner_on_unreachable', 'Host Unreachable'), ('runner_on_no_hosts', 'No Hosts Remaining'), ('runner_on_async_poll', 'Host Polling'), ('runner_on_async_ok', 'Host Async OK'), ('runner_on_async_failed', 'Host Async Failure'), ('runner_on_file_diff', 'File Difference'), ('playbook_on_start', 'Playbook Started'), ('playbook_on_notify', 'Running Handlers'), ('playbook_on_no_hosts_matched', 'No Hosts Matched'), ('playbook_on_no_hosts_remaining', 'No Hosts Remaining'), ('playbook_on_task_start', 'Task Started'), ('playbook_on_vars_prompt', 'Variables Prompted'), ('playbook_on_setup', 'Gathering Facts'), ('playbook_on_import_for_host', 'internal: on Import for Host'), ('playbook_on_not_import_for_host', 'internal: on Not Import for Host'), ('playbook_on_play_start', 'Play Started'), ('playbook_on_stats', 'Playbook Complete')])),
- ('event_data', jsonfield.fields.JSONField(default={}, blank=True)),
+ ('event_data', jsonfield.fields.JSONField(default=dict, blank=True)),
('failed', models.BooleanField(default=False, editable=False)),
('changed', models.BooleanField(default=False, editable=False)),
('host_name', models.CharField(default='', max_length=1024, editable=False)),
@@ -241,7 +241,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('created', models.DateTimeField(auto_now_add=True)),
('modified', models.DateTimeField(auto_now=True)),
- ('instance', models.ForeignKey(to='main.Instance')),
+ ('instance', models.ForeignKey(on_delete=models.CASCADE, to='main.Instance')),
],
),
migrations.CreateModel(
@@ -287,7 +287,7 @@ class Migration(migrations.Migration):
('created', models.DateTimeField(default=None, editable=False)),
('modified', models.DateTimeField(default=None, editable=False)),
('ldap_dn', models.CharField(default='', max_length=1024)),
- ('user', awx.main.fields.AutoOneToOneField(related_name='profile', editable=False, to=settings.AUTH_USER_MODEL)),
+ ('user', awx.main.fields.AutoOneToOneField(related_name='profile', editable=False, on_delete=models.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
@@ -304,7 +304,7 @@ class Migration(migrations.Migration):
('dtend', models.DateTimeField(default=None, null=True, editable=False)),
('rrule', models.CharField(max_length=255)),
('next_run', models.DateTimeField(default=None, null=True, editable=False)),
- ('extra_data', jsonfield.fields.JSONField(default={}, blank=True)),
+ ('extra_data', jsonfield.fields.JSONField(default=dict, blank=True)),
('created_by', models.ForeignKey(related_name="{u'class': 'schedule', u'app_label': 'main'}(class)s_created+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
('modified_by', models.ForeignKey(related_name="{u'class': 'schedule', u'app_label': 'main'}(class)s_modified+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')),
@@ -343,7 +343,7 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=512)),
('old_pk', models.PositiveIntegerField(default=None, null=True, editable=False)),
('launch_type', models.CharField(default='manual', max_length=20, editable=False, choices=[('manual', 'Manual'), ('relaunch', 'Relaunch'), ('callback', 'Callback'), ('scheduled', 'Scheduled'), ('dependency', 'Dependency')])),
- ('cancel_flag', models.BooleanField(default=False, editable=False)),
+ ('cancel_flag', models.BooleanField(blank=True, default=False, editable=False)),
('status', models.CharField(default='new', max_length=20, editable=False, choices=[('new', 'New'), ('pending', 'Pending'), ('waiting', 'Waiting'), ('running', 'Running'), ('successful', 'Successful'), ('failed', 'Failed'), ('error', 'Error'), ('canceled', 'Canceled')])),
('failed', models.BooleanField(default=False, editable=False)),
('started', models.DateTimeField(default=None, null=True, editable=False)),
@@ -351,7 +351,7 @@ class Migration(migrations.Migration):
('elapsed', models.DecimalField(editable=False, max_digits=12, decimal_places=3)),
('job_args', models.TextField(default='', editable=False, blank=True)),
('job_cwd', models.CharField(default='', max_length=1024, editable=False, blank=True)),
- ('job_env', jsonfield.fields.JSONField(default={}, editable=False, blank=True)),
+ ('job_env', jsonfield.fields.JSONField(default=dict, editable=False, blank=True)),
('job_explanation', 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)),
@@ -380,7 +380,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='AdHocCommand',
fields=[
- ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJob')),
+ ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJob')),
('job_type', models.CharField(default='run', max_length=64, choices=[('run', 'Run'), ('check', 'Check')])),
('limit', models.CharField(default='', max_length=1024, blank=True)),
('module_name', models.CharField(default='', max_length=1024, blank=True)),
@@ -394,7 +394,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='InventorySource',
fields=[
- ('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJobTemplate')),
+ ('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJobTemplate')),
('source', models.CharField(default='', max_length=32, blank=True, choices=[('', 'Manual'), ('file', 'Local File, Directory or Script'), ('rax', 'Rackspace Cloud Servers'), ('ec2', 'Amazon EC2'), ('gce', 'Google Compute Engine'), ('azure', 'Microsoft Azure'), ('vmware', 'VMware vCenter'), ('openstack', 'OpenStack'), ('custom', 'Custom Script')])),
('source_path', models.CharField(default='', max_length=1024, editable=False, blank=True)),
('source_vars', models.TextField(default='', help_text='Inventory source variables in YAML or JSON format.', blank=True)),
@@ -411,7 +411,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='InventoryUpdate',
fields=[
- ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJob')),
+ ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJob')),
('source', models.CharField(default='', max_length=32, blank=True, choices=[('', 'Manual'), ('file', 'Local File, Directory or Script'), ('rax', 'Rackspace Cloud Servers'), ('ec2', 'Amazon EC2'), ('gce', 'Google Compute Engine'), ('azure', 'Microsoft Azure'), ('vmware', 'VMware vCenter'), ('openstack', 'OpenStack'), ('custom', 'Custom Script')])),
('source_path', models.CharField(default='', max_length=1024, editable=False, blank=True)),
('source_vars', models.TextField(default='', help_text='Inventory source variables in YAML or JSON format.', blank=True)),
@@ -427,7 +427,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Job',
fields=[
- ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJob')),
+ ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJob')),
('job_type', models.CharField(default='run', max_length=64, choices=[('run', 'Run'), ('check', 'Check'), ('scan', 'Scan')])),
('playbook', models.CharField(default='', max_length=1024, blank=True)),
('forks', models.PositiveIntegerField(default=0, blank=True)),
@@ -435,7 +435,7 @@ class Migration(migrations.Migration):
('verbosity', models.PositiveIntegerField(default=0, blank=True, choices=[(0, '0 (Normal)'), (1, '1 (Verbose)'), (2, '2 (More Verbose)'), (3, '3 (Debug)'), (4, '4 (Connection Debug)'), (5, '5 (WinRM Debug)')])),
('extra_vars', models.TextField(default='', blank=True)),
('job_tags', models.CharField(default='', max_length=1024, blank=True)),
- ('force_handlers', models.BooleanField(default=False)),
+ ('force_handlers', models.BooleanField(blank=True, default=False)),
('skip_tags', models.CharField(default='', max_length=1024, blank=True)),
('start_at_task', models.CharField(default='', max_length=1024, blank=True)),
('become_enabled', models.BooleanField(default=False)),
@@ -448,7 +448,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='JobTemplate',
fields=[
- ('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJobTemplate')),
+ ('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJobTemplate')),
('job_type', models.CharField(default='run', max_length=64, choices=[('run', 'Run'), ('check', 'Check'), ('scan', 'Scan')])),
('playbook', models.CharField(default='', max_length=1024, blank=True)),
('forks', models.PositiveIntegerField(default=0, blank=True)),
@@ -456,14 +456,14 @@ class Migration(migrations.Migration):
('verbosity', models.PositiveIntegerField(default=0, blank=True, choices=[(0, '0 (Normal)'), (1, '1 (Verbose)'), (2, '2 (More Verbose)'), (3, '3 (Debug)'), (4, '4 (Connection Debug)'), (5, '5 (WinRM Debug)')])),
('extra_vars', models.TextField(default='', blank=True)),
('job_tags', models.CharField(default='', max_length=1024, blank=True)),
- ('force_handlers', models.BooleanField(default=False)),
+ ('force_handlers', models.BooleanField(blank=True, default=False)),
('skip_tags', models.CharField(default='', max_length=1024, blank=True)),
('start_at_task', models.CharField(default='', max_length=1024, blank=True)),
('become_enabled', models.BooleanField(default=False)),
('host_config_key', models.CharField(default='', max_length=1024, blank=True)),
('ask_variables_on_launch', models.BooleanField(default=False)),
('survey_enabled', models.BooleanField(default=False)),
- ('survey_spec', jsonfield.fields.JSONField(default={}, blank=True)),
+ ('survey_spec', jsonfield.fields.JSONField(default=dict, blank=True)),
],
options={
'ordering': ('name',),
@@ -473,7 +473,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Project',
fields=[
- ('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJobTemplate')),
+ ('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJobTemplate')),
('local_path', models.CharField(help_text='Local path (relative to PROJECTS_ROOT) containing playbooks and related files for this project.', max_length=1024, blank=True)),
('scm_type', models.CharField(default='', max_length=8, verbose_name='SCM Type', blank=True, choices=[('', 'Manual'), ('git', 'Git'), ('hg', 'Mercurial'), ('svn', 'Subversion')])),
('scm_url', models.CharField(default='', max_length=1024, verbose_name='SCM URL', blank=True)),
@@ -492,7 +492,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='ProjectUpdate',
fields=[
- ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJob')),
+ ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJob')),
('local_path', models.CharField(help_text='Local path (relative to PROJECTS_ROOT) containing playbooks and related files for this project.', max_length=1024, blank=True)),
('scm_type', models.CharField(default='', max_length=8, verbose_name='SCM Type', blank=True, choices=[('', 'Manual'), ('git', 'Git'), ('hg', 'Mercurial'), ('svn', 'Subversion')])),
('scm_url', models.CharField(default='', max_length=1024, verbose_name='SCM URL', blank=True)),
@@ -505,7 +505,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='SystemJob',
fields=[
- ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJob')),
+ ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJob')),
('job_type', models.CharField(default='', max_length=32, blank=True, choices=[('cleanup_jobs', 'Remove jobs older than a certain number of days'), ('cleanup_activitystream', 'Remove activity stream entries older than a certain number of days'), ('cleanup_deleted', 'Purge previously deleted items from the database'), ('cleanup_facts', 'Purge and/or reduce the granularity of system tracking data')])),
('extra_vars', models.TextField(default='', blank=True)),
],
@@ -517,7 +517,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='SystemJobTemplate',
fields=[
- ('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJobTemplate')),
+ ('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJobTemplate')),
('job_type', models.CharField(default='', max_length=32, blank=True, choices=[('cleanup_jobs', 'Remove jobs older than a certain number of days'), ('cleanup_activitystream', 'Remove activity stream entries older than a certain number of days'), ('cleanup_deleted', 'Purge previously deleted items from the database'), ('cleanup_facts', 'Purge and/or reduce the granularity of system tracking data')])),
],
bases=('main.unifiedjobtemplate', models.Model),
@@ -550,7 +550,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='unifiedjobtemplate',
name='polymorphic_ctype',
- field=models.ForeignKey(related_name='polymorphic_main.unifiedjobtemplate_set+', editable=False, to='contenttypes.ContentType', null=True),
+ field=models.ForeignKey(related_name='polymorphic_main.unifiedjobtemplate_set+', editable=False, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType', null=True),
),
migrations.AddField(
model_name='unifiedjobtemplate',
@@ -575,7 +575,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='unifiedjob',
name='polymorphic_ctype',
- field=models.ForeignKey(related_name='polymorphic_main.unifiedjob_set+', editable=False, to='contenttypes.ContentType', null=True),
+ field=models.ForeignKey(related_name='polymorphic_main.unifiedjob_set+', editable=False, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType', null=True),
),
migrations.AddField(
model_name='unifiedjob',
@@ -595,7 +595,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='schedule',
name='unified_job_template',
- field=models.ForeignKey(related_name='schedules', to='main.UnifiedJobTemplate'),
+ field=models.ForeignKey(related_name='schedules', on_delete=django.db.models.deletion.CASCADE, to='main.UnifiedJobTemplate'),
),
migrations.AddField(
model_name='permission',
@@ -610,12 +610,12 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='joborigin',
name='unified_job',
- field=models.OneToOneField(related_name='job_origin', to='main.UnifiedJob'),
+ field=models.OneToOneField(related_name='job_origin', on_delete=django.db.models.deletion.CASCADE, to='main.UnifiedJob'),
),
migrations.AddField(
model_name='inventory',
name='organization',
- field=models.ForeignKey(related_name='inventories', to='main.Organization', help_text='Organization containing this inventory.'),
+ field=models.ForeignKey(related_name='inventories', on_delete=django.db.models.deletion.CASCADE, to='main.Organization', help_text='Organization containing this inventory.'),
),
migrations.AddField(
model_name='inventory',
@@ -625,7 +625,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='host',
name='inventory',
- field=models.ForeignKey(related_name='hosts', to='main.Inventory'),
+ field=models.ForeignKey(related_name='hosts', on_delete=django.db.models.deletion.CASCADE, to='main.Inventory'),
),
migrations.AddField(
model_name='host',
@@ -650,7 +650,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='group',
name='inventory',
- field=models.ForeignKey(related_name='groups', to='main.Inventory'),
+ field=models.ForeignKey(related_name='groups', on_delete=django.db.models.deletion.CASCADE, to='main.Inventory'),
),
migrations.AddField(
model_name='group',
@@ -680,12 +680,12 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='credential',
name='team',
- field=models.ForeignKey(related_name='credentials', default=None, blank=True, to='main.Team', null=True),
+ field=models.ForeignKey(related_name='credentials', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to='main.Team', null=True),
),
migrations.AddField(
model_name='credential',
name='user',
- field=models.ForeignKey(related_name='credentials', default=None, blank=True, to=settings.AUTH_USER_MODEL, null=True),
+ field=models.ForeignKey(related_name='credentials', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to=settings.AUTH_USER_MODEL, null=True),
),
migrations.AddField(
model_name='adhoccommandevent',
@@ -774,7 +774,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='projectupdate',
name='project',
- field=models.ForeignKey(related_name='project_updates', editable=False, to='main.Project'),
+ field=models.ForeignKey(related_name='project_updates', on_delete=django.db.models.deletion.CASCADE, editable=False, to='main.Project'),
),
migrations.AddField(
model_name='project',
@@ -814,12 +814,12 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='jobhostsummary',
name='job',
- field=models.ForeignKey(related_name='job_host_summaries', editable=False, to='main.Job'),
+ field=models.ForeignKey(related_name='job_host_summaries', on_delete=django.db.models.deletion.CASCADE, editable=False, to='main.Job'),
),
migrations.AddField(
model_name='jobevent',
name='job',
- field=models.ForeignKey(related_name='job_events', editable=False, to='main.Job'),
+ field=models.ForeignKey(related_name='job_events', on_delete=django.db.models.deletion.CASCADE, editable=False, to='main.Job'),
),
migrations.AddField(
model_name='job',
@@ -859,7 +859,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='inventoryupdate',
name='inventory_source',
- field=models.ForeignKey(related_name='inventory_updates', editable=False, to='main.InventorySource'),
+ field=models.ForeignKey(related_name='inventory_updates', on_delete=django.db.models.deletion.CASCADE, editable=False, to='main.InventorySource'),
),
migrations.AddField(
model_name='inventoryupdate',
@@ -874,12 +874,12 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='inventorysource',
name='group',
- field=awx.main.fields.AutoOneToOneField(related_name='inventory_source', null=True, default=None, editable=False, to='main.Group'),
+ field=awx.main.fields.AutoOneToOneField(related_name='inventory_source', on_delete=django.db.models.deletion.SET_NULL, null=True, default=None, editable=False, to='main.Group'),
),
migrations.AddField(
model_name='inventorysource',
name='inventory',
- field=models.ForeignKey(related_name='inventory_sources', default=None, editable=False, to='main.Inventory', null=True),
+ field=models.ForeignKey(related_name='inventory_sources', on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to='main.Inventory', null=True),
),
migrations.AddField(
model_name='inventorysource',
@@ -916,7 +916,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='adhoccommandevent',
name='ad_hoc_command',
- field=models.ForeignKey(related_name='ad_hoc_command_events', editable=False, to='main.AdHocCommand'),
+ field=models.ForeignKey(related_name='ad_hoc_command_events', on_delete=django.db.models.deletion.CASCADE, editable=False, to='main.AdHocCommand'),
),
migrations.AddField(
model_name='adhoccommand',
diff --git a/awx/main/migrations/0002_squashed_v300_release.py b/awx/main/migrations/0002_squashed_v300_release.py
index 11190a20da..89fce679ea 100644
--- a/awx/main/migrations/0002_squashed_v300_release.py
+++ b/awx/main/migrations/0002_squashed_v300_release.py
@@ -13,7 +13,6 @@ from django.conf import settings
from django.utils.timezone import now
import jsonfield.fields
-import jsonbfield.fields
import taggit.managers
@@ -144,7 +143,7 @@ class Migration(migrations.Migration):
('category', models.CharField(max_length=128)),
('value', models.TextField(blank=True)),
('value_type', models.CharField(max_length=12, choices=[('string', 'String'), ('int', 'Integer'), ('float', 'Decimal'), ('json', 'JSON'), ('bool', 'Boolean'), ('password', 'Password'), ('list', 'List')])),
- ('user', models.ForeignKey(related_name='settings', default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
+ ('user', models.ForeignKey(related_name='settings', default=None, editable=False, to=settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True)),
],
),
# Notification changes
@@ -185,7 +184,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='notification',
name='notification_template',
- field=models.ForeignKey(related_name='notifications', editable=False, to='main.NotificationTemplate'),
+ field=models.ForeignKey(related_name='notifications', editable=False, on_delete=models.CASCADE, to='main.NotificationTemplate'),
),
migrations.AddField(
model_name='activitystream',
@@ -239,8 +238,8 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('timestamp', models.DateTimeField(default=None, help_text='Date and time of the corresponding fact scan gathering time.', editable=False)),
('module', models.CharField(max_length=128)),
- ('facts', jsonbfield.fields.JSONField(default={}, help_text='Arbitrary JSON structure of module facts captured at timestamp for a single host.', blank=True)),
- ('host', models.ForeignKey(related_name='facts', to='main.Host', help_text='Host for the facts that the fact scan captured.')),
+ ('facts', awx.main.fields.JSONBField(default=dict, help_text='Arbitrary JSON structure of module facts captured at timestamp for a single host.', blank=True)),
+ ('host', models.ForeignKey(related_name='facts', to='main.Host', on_delete=models.CASCADE, help_text='Host for the facts that the fact scan captured.')),
],
),
migrations.AlterIndexTogether(
@@ -318,7 +317,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='project',
name='organization',
- field=models.ForeignKey(related_name='projects', to='main.Organization', blank=True, null=True),
+ field=models.ForeignKey(related_name='projects', to='main.Organization', on_delete=models.CASCADE, blank=True, null=True),
),
migrations.AlterField(
model_name='team',
@@ -367,7 +366,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='credential',
name='organization',
- field=models.ForeignKey(related_name='credentials', default=None, blank=True, to='main.Organization', null=True),
+ field=models.ForeignKey(related_name='credentials', on_delete=models.CASCADE, default=None, blank=True, to='main.Organization', null=True),
),
#
@@ -382,7 +381,7 @@ class Migration(migrations.Migration):
('members', models.ManyToManyField(related_name='roles', to=settings.AUTH_USER_MODEL)),
('parents', models.ManyToManyField(related_name='children', to='main.Role')),
('implicit_parents', models.TextField(default='[]')),
- ('content_type', models.ForeignKey(default=None, to='contenttypes.ContentType', null=True)),
+ ('content_type', models.ForeignKey(default=None, to='contenttypes.ContentType', on_delete=models.CASCADE, null=True)),
('object_id', models.PositiveIntegerField(default=None, null=True)),
],
@@ -398,8 +397,8 @@ class Migration(migrations.Migration):
('role_field', models.TextField()),
('content_type_id', models.PositiveIntegerField()),
('object_id', models.PositiveIntegerField()),
- ('ancestor', models.ForeignKey(related_name='+', to='main.Role')),
- ('descendent', models.ForeignKey(related_name='+', to='main.Role')),
+ ('ancestor', models.ForeignKey(on_delete=models.CASCADE, related_name='+', to='main.Role')),
+ ('descendent', models.ForeignKey(on_delete=models.CASCADE, related_name='+', to='main.Role')),
],
options={
'db_table': 'main_rbac_role_ancestors',
@@ -569,7 +568,7 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=512)),
('created_by', models.ForeignKey(related_name="{u'class': 'label', u'app_label': 'main'}(class)s_created+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
('modified_by', models.ForeignKey(related_name="{u'class': 'label', u'app_label': 'main'}(class)s_modified+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
- ('organization', models.ForeignKey(related_name='labels', to='main.Organization', help_text='Organization this label belongs to.')),
+ ('organization', models.ForeignKey(related_name='labels', on_delete=django.db.models.deletion.CASCADE, to='main.Organization', help_text='Organization this label belongs to.')),
('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')),
],
options={
@@ -599,12 +598,12 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='label',
name='organization',
- field=models.ForeignKey(related_name='labels', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to='main.Organization', help_text='Organization this label belongs to.', null=True),
+ field=models.ForeignKey(related_name='labels', on_delete=django.db.models.deletion.CASCADE, default=None, blank=True, to='main.Organization', help_text='Organization this label belongs to.', null=True),
),
migrations.AlterField(
model_name='label',
name='organization',
- field=models.ForeignKey(related_name='labels', to='main.Organization', help_text='Organization this label belongs to.'),
+ field=models.ForeignKey(related_name='labels', on_delete=django.db.models.deletion.CASCADE, to='main.Organization', help_text='Organization this label belongs to.'),
),
# InventorySource Credential
migrations.AddField(
@@ -630,12 +629,12 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='credential',
name='deprecated_team',
- field=models.ForeignKey(related_name='deprecated_credentials', default=None, blank=True, to='main.Team', null=True),
+ field=models.ForeignKey(related_name='deprecated_credentials', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to='main.Team', null=True),
),
migrations.AlterField(
model_name='credential',
name='deprecated_user',
- field=models.ForeignKey(related_name='deprecated_credentials', default=None, blank=True, to=settings.AUTH_USER_MODEL, null=True),
+ field=models.ForeignKey(related_name='deprecated_credentials', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to=settings.AUTH_USER_MODEL, null=True),
),
migrations.AlterField(
model_name='credential',
diff --git a/awx/main/migrations/0003_squashed_v300_v303_updates.py b/awx/main/migrations/0003_squashed_v300_v303_updates.py
index 3c3680a4e7..d58ffc2dfe 100644
--- a/awx/main/migrations/0003_squashed_v300_v303_updates.py
+++ b/awx/main/migrations/0003_squashed_v300_v303_updates.py
@@ -116,7 +116,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='team',
name='organization',
- field=models.ForeignKey(related_name='teams', to='main.Organization'),
+ field=models.ForeignKey(related_name='teams', on_delete=models.CASCADE, to='main.Organization'),
preserve_default=False,
),
] + _squashed.operations(SQUASHED_30, applied=True)
diff --git a/awx/main/migrations/0004_squashed_v310_release.py b/awx/main/migrations/0004_squashed_v310_release.py
index 965b5184d7..88a33d146c 100644
--- a/awx/main/migrations/0004_squashed_v310_release.py
+++ b/awx/main/migrations/0004_squashed_v310_release.py
@@ -74,7 +74,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='WorkflowJob',
fields=[
- ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJob')),
+ ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, on_delete=models.CASCADE, primary_key=True, serialize=False, to='main.UnifiedJob')),
('extra_vars', models.TextField(default='', blank=True)),
],
options={
@@ -100,7 +100,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='WorkflowJobTemplate',
fields=[
- ('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJobTemplate')),
+ ('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=models.CASCADE, serialize=False, to='main.UnifiedJobTemplate')),
('extra_vars', models.TextField(default='', blank=True)),
('admin_role', awx.main.fields.ImplicitRoleField(related_name='+', parent_role='singleton:system_administrator', to='main.Role', null='True')),
],
@@ -116,7 +116,7 @@ class Migration(migrations.Migration):
('failure_nodes', models.ManyToManyField(related_name='workflowjobtemplatenodes_failure', to='main.WorkflowJobTemplateNode', blank=True)),
('success_nodes', models.ManyToManyField(related_name='workflowjobtemplatenodes_success', to='main.WorkflowJobTemplateNode', blank=True)),
('unified_job_template', models.ForeignKey(related_name='workflowjobtemplatenodes', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to='main.UnifiedJobTemplate', null=True)),
- ('workflow_job_template', models.ForeignKey(related_name='workflow_job_template_nodes', default=None, blank=True, to='main.WorkflowJobTemplate', null=True)),
+ ('workflow_job_template', models.ForeignKey(related_name='workflow_job_template_nodes', on_delete=models.SET_NULL, default=None, blank=True, to='main.WorkflowJobTemplate', null=True)),
],
options={
'abstract': False,
@@ -161,7 +161,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='workflowjobnode',
name='char_prompts',
- field=jsonfield.fields.JSONField(default={}, blank=True),
+ field=jsonfield.fields.JSONField(default=dict, blank=True),
),
migrations.AddField(
model_name='workflowjobnode',
@@ -191,7 +191,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='workflowjobtemplatenode',
name='char_prompts',
- field=jsonfield.fields.JSONField(default={}, blank=True),
+ field=jsonfield.fields.JSONField(default=dict, blank=True),
),
migrations.AddField(
model_name='workflowjobtemplatenode',
@@ -211,7 +211,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='workflowjobnode',
name='workflow_job',
- field=models.ForeignKey(related_name='workflow_job_nodes', default=None, blank=True, to='main.WorkflowJob', null=True),
+ field=models.ForeignKey(related_name='workflow_job_nodes', on_delete=django.db.models.deletion.CASCADE, default=None, blank=True, to='main.WorkflowJob', null=True),
),
migrations.AlterField(
model_name='workflowjobtemplate',
@@ -227,12 +227,12 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='job',
name='artifacts',
- field=jsonfield.fields.JSONField(default={}, editable=False, blank=True),
+ field=jsonfield.fields.JSONField(default=dict, editable=False, blank=True),
),
migrations.AddField(
model_name='workflowjobnode',
name='ancestor_artifacts',
- field=jsonfield.fields.JSONField(default={}, editable=False, blank=True),
+ field=jsonfield.fields.JSONField(default=dict, editable=False, blank=True),
),
# Job timeout settings
migrations.AddField(
@@ -397,7 +397,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='workflowjob',
name='survey_passwords',
- field=jsonfield.fields.JSONField(default={}, editable=False, blank=True),
+ field=jsonfield.fields.JSONField(default=dict, editable=False, blank=True),
),
migrations.AddField(
model_name='workflowjobtemplate',
@@ -407,33 +407,33 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='workflowjobtemplate',
name='survey_spec',
- field=jsonfield.fields.JSONField(default={}, blank=True),
+ field=jsonfield.fields.JSONField(default=dict, blank=True),
),
# JSON field changes
migrations.AlterField(
model_name='adhoccommandevent',
name='event_data',
- field=awx.main.fields.JSONField(default={}, blank=True),
+ field=awx.main.fields.JSONField(default=dict, blank=True),
),
migrations.AlterField(
model_name='job',
name='artifacts',
- field=awx.main.fields.JSONField(default={}, editable=False, blank=True),
+ field=awx.main.fields.JSONField(default=dict, editable=False, blank=True),
),
migrations.AlterField(
model_name='job',
name='survey_passwords',
- field=awx.main.fields.JSONField(default={}, editable=False, blank=True),
+ field=awx.main.fields.JSONField(default=dict, editable=False, blank=True),
),
migrations.AlterField(
model_name='jobevent',
name='event_data',
- field=awx.main.fields.JSONField(default={}, blank=True),
+ field=awx.main.fields.JSONField(default=dict, blank=True),
),
migrations.AlterField(
model_name='jobtemplate',
name='survey_spec',
- field=awx.main.fields.JSONField(default={}, blank=True),
+ field=awx.main.fields.JSONField(default=dict, blank=True),
),
migrations.AlterField(
model_name='notification',
@@ -453,37 +453,37 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='schedule',
name='extra_data',
- field=awx.main.fields.JSONField(default={}, blank=True),
+ field=awx.main.fields.JSONField(default=dict, blank=True),
),
migrations.AlterField(
model_name='unifiedjob',
name='job_env',
- field=awx.main.fields.JSONField(default={}, editable=False, blank=True),
+ field=awx.main.fields.JSONField(default=dict, editable=False, blank=True),
),
migrations.AlterField(
model_name='workflowjob',
name='survey_passwords',
- field=awx.main.fields.JSONField(default={}, editable=False, blank=True),
+ field=awx.main.fields.JSONField(default=dict, editable=False, blank=True),
),
migrations.AlterField(
model_name='workflowjobnode',
name='ancestor_artifacts',
- field=awx.main.fields.JSONField(default={}, editable=False, blank=True),
+ field=awx.main.fields.JSONField(default=dict, editable=False, blank=True),
),
migrations.AlterField(
model_name='workflowjobnode',
name='char_prompts',
- field=awx.main.fields.JSONField(default={}, blank=True),
+ field=awx.main.fields.JSONField(default=dict, blank=True),
),
migrations.AlterField(
model_name='workflowjobtemplate',
name='survey_spec',
- field=awx.main.fields.JSONField(default={}, blank=True),
+ field=awx.main.fields.JSONField(default=dict, blank=True),
),
migrations.AlterField(
model_name='workflowjobtemplatenode',
name='char_prompts',
- field=awx.main.fields.JSONField(default={}, blank=True),
+ field=awx.main.fields.JSONField(default=dict, blank=True),
),
# Job Project Update
migrations.AddField(
diff --git a/awx/main/migrations/0006_v320_release.py b/awx/main/migrations/0006_v320_release.py
index cda08b98c3..d5712f8e0a 100644
--- a/awx/main/migrations/0006_v320_release.py
+++ b/awx/main/migrations/0006_v320_release.py
@@ -55,12 +55,12 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='inventorysource',
name='deprecated_group',
- field=models.OneToOneField(related_name='deprecated_inventory_source', null=True, default=None, to='main.Group'),
+ field=models.OneToOneField(related_name='deprecated_inventory_source', on_delete=models.CASCADE, null=True, default=None, to='main.Group'),
),
migrations.AlterField(
model_name='inventorysource',
name='inventory',
- field=models.ForeignKey(related_name='inventory_sources', default=None, to='main.Inventory', null=True),
+ field=models.ForeignKey(related_name='inventory_sources', default=None, to='main.Inventory', on_delete=models.CASCADE, null=True),
),
# Smart Inventory
@@ -78,13 +78,13 @@ class Migration(migrations.Migration):
name='SmartInventoryMembership',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
- ('host', models.ForeignKey(related_name='+', to='main.Host')),
+ ('host', models.ForeignKey(related_name='+', on_delete=models.CASCADE, to='main.Host')),
],
),
migrations.AddField(
model_name='smartinventorymembership',
name='inventory',
- field=models.ForeignKey(related_name='+', to='main.Inventory'),
+ field=models.ForeignKey(on_delete=models.CASCADE, related_name='+', to='main.Inventory'),
),
migrations.AddField(
model_name='host',
@@ -105,19 +105,19 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='inventory',
name='organization',
- field=models.ForeignKey(related_name='inventories', on_delete=models.deletion.SET_NULL, to='main.Organization', help_text='Organization containing this inventory.', null=True),
+ field=models.ForeignKey(related_name='inventories', on_delete=models.SET_NULL, to='main.Organization', help_text='Organization containing this inventory.', null=True),
),
# Facts
migrations.AlterField(
model_name='fact',
name='facts',
- field=awx.main.fields.JSONBField(default={}, help_text='Arbitrary JSON structure of module facts captured at timestamp for a single host.', blank=True),
+ field=awx.main.fields.JSONBField(default=dict, help_text='Arbitrary JSON structure of module facts captured at timestamp for a single host.', blank=True),
),
migrations.AddField(
model_name='host',
name='ansible_facts',
- field=awx.main.fields.JSONBField(default={}, help_text='Arbitrary JSON structure of most recent ansible_facts, per-host.', blank=True),
+ field=awx.main.fields.JSONBField(default=dict, help_text='Arbitrary JSON structure of most recent ansible_facts, per-host.', blank=True),
),
migrations.AddField(
model_name='host',
@@ -148,12 +148,12 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='inventorysource',
name='source_project',
- field=models.ForeignKey(related_name='scm_inventory_sources', default=None, blank=True, to='main.Project', help_text='Project containing inventory file used as source.', null=True),
+ field=models.ForeignKey(related_name='scm_inventory_sources', on_delete=models.CASCADE, default=None, blank=True, to='main.Project', help_text='Project containing inventory file used as source.', null=True),
),
migrations.AddField(
model_name='inventoryupdate',
name='source_project_update',
- field=models.ForeignKey(related_name='scm_inventory_updates', default=None, blank=True, to='main.ProjectUpdate', help_text='Inventory files from this Project Update were used for the inventory update.', null=True),
+ field=models.ForeignKey(related_name='scm_inventory_updates', on_delete=models.CASCADE, default=None, blank=True, to='main.ProjectUpdate', help_text='Inventory files from this Project Update were used for the inventory update.', null=True),
),
migrations.AddField(
model_name='project',
@@ -200,7 +200,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='notificationtemplate',
name='organization',
- field=models.ForeignKey(related_name='notification_templates', to='main.Organization', null=True),
+ field=models.ForeignKey(related_name='notification_templates', on_delete=models.CASCADE, to='main.Organization', null=True),
),
migrations.AlterUniqueTogether(
name='notificationtemplate',
@@ -312,7 +312,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='inventory',
name='insights_credential',
- field=models.ForeignKey(related_name='insights_inventories', on_delete=models.deletion.SET_NULL, default=None, blank=True, to='main.Credential', help_text='Credentials to be used by hosts belonging to this inventory when accessing Red Hat Insights API.', null=True),
+ field=models.ForeignKey(related_name='insights_inventories', on_delete=models.SET_NULL, default=None, blank=True, to='main.Credential', help_text='Credentials to be used by hosts belonging to this inventory when accessing Red Hat Insights API.', null=True),
),
migrations.AlterField(
model_name='inventory',
@@ -382,10 +382,10 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=512)),
('kind', models.CharField(max_length=32, choices=[('ssh', 'Machine'), ('vault', 'Vault'), ('net', 'Network'), ('scm', 'Source Control'), ('cloud', 'Cloud'), ('insights', 'Insights')])),
('managed_by_tower', models.BooleanField(default=False, editable=False)),
- ('inputs', awx.main.fields.CredentialTypeInputField(default={}, blank=True, help_text='Enter inputs using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.')),
- ('injectors', awx.main.fields.CredentialTypeInjectorField(default={}, blank=True, help_text='Enter injectors using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.')),
- ('created_by', models.ForeignKey(related_name="{u'class': 'credentialtype', u'app_label': 'main'}(class)s_created+", on_delete=models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
- ('modified_by', models.ForeignKey(related_name="{u'class': 'credentialtype', u'app_label': 'main'}(class)s_modified+", on_delete=models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
+ ('inputs', awx.main.fields.CredentialTypeInputField(default=dict, blank=True, help_text='Enter inputs using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.')),
+ ('injectors', awx.main.fields.CredentialTypeInjectorField(default=dict, blank=True, help_text='Enter injectors using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.')),
+ ('created_by', models.ForeignKey(related_name="{u'class': 'credentialtype', u'app_label': 'main'}(class)s_created+", on_delete=models.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
+ ('modified_by', models.ForeignKey(related_name="{u'class': 'credentialtype', u'app_label': 'main'}(class)s_modified+", on_delete=models.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')),
],
options={
@@ -399,23 +399,23 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='credential',
name='inputs',
- field=awx.main.fields.CredentialInputField(default={}, blank=True),
+ field=awx.main.fields.CredentialInputField(default=dict, blank=True),
),
migrations.AddField(
model_name='credential',
name='credential_type',
- field=models.ForeignKey(related_name='credentials', to='main.CredentialType', null=True),
+ field=models.ForeignKey(related_name='credentials', on_delete=models.CASCADE, to='main.CredentialType', null=True),
preserve_default=False,
),
migrations.AddField(
model_name='job',
name='vault_credential',
- field=models.ForeignKey(related_name='jobs_as_vault_credential+', on_delete=models.deletion.SET_NULL, default=None, blank=True, to='main.Credential', null=True),
+ field=models.ForeignKey(related_name='jobs_as_vault_credential+', on_delete=models.SET_NULL, default=None, blank=True, to='main.Credential', null=True),
),
migrations.AddField(
model_name='jobtemplate',
name='vault_credential',
- field=models.ForeignKey(related_name='jobtemplates_as_vault_credential+', on_delete=models.deletion.SET_NULL, default=None, blank=True, to='main.Credential', null=True),
+ field=models.ForeignKey(related_name='jobtemplates_as_vault_credential+', on_delete=models.SET_NULL, default=None, blank=True, to='main.Credential', null=True),
),
migrations.AddField(
model_name='job',
@@ -452,7 +452,7 @@ class Migration(migrations.Migration):
('name', models.CharField(unique=True, max_length=250)),
('created', models.DateTimeField(auto_now_add=True)),
('modified', models.DateTimeField(auto_now=True)),
- ('controller', models.ForeignKey(related_name='controlled_groups', default=None, editable=False, to='main.InstanceGroup', help_text='Instance Group to remotely control this group.', null=True)),
+ ('controller', models.ForeignKey(related_name='controlled_groups', on_delete=models.CASCADE, default=None, editable=False, to='main.InstanceGroup', help_text='Instance Group to remotely control this group.', null=True)),
('instances', models.ManyToManyField(help_text='Instances that are members of this InstanceGroup', related_name='rampart_groups', editable=False, to='main.Instance')),
],
),
@@ -464,7 +464,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='unifiedjob',
name='instance_group',
- field=models.ForeignKey(on_delete=models.deletion.SET_NULL, default=None, blank=True, to='main.InstanceGroup', help_text='The Rampart/Instance group the job was run under', null=True),
+ field=models.ForeignKey(on_delete=models.SET_NULL, default=None, blank=True, to='main.InstanceGroup', help_text='The Rampart/Instance group the job was run under', null=True),
),
migrations.AddField(
model_name='unifiedjobtemplate',
diff --git a/awx/main/migrations/0008_v320_drop_v1_credential_fields.py b/awx/main/migrations/0008_v320_drop_v1_credential_fields.py
index ed45bc4b8e..f2fc44397e 100644
--- a/awx/main/migrations/0008_v320_drop_v1_credential_fields.py
+++ b/awx/main/migrations/0008_v320_drop_v1_credential_fields.py
@@ -103,12 +103,12 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='credential',
name='credential_type',
- field=models.ForeignKey(related_name='credentials', to='main.CredentialType', null=False, help_text='Specify the type of credential you want to create. Refer to the Ansible Tower documentation for details on each type.')
+ field=models.ForeignKey(related_name='credentials', to='main.CredentialType', on_delete=models.CASCADE, null=False, help_text='Specify the type of credential you want to create. Refer to the Ansible Tower documentation for details on each type.')
),
migrations.AlterField(
model_name='credential',
name='inputs',
- field=awx.main.fields.CredentialInputField(default={}, help_text='Enter inputs using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.', blank=True),
+ field=awx.main.fields.CredentialInputField(default=dict, help_text='Enter inputs using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.', blank=True),
),
migrations.RemoveField(
model_name='job',
diff --git a/awx/main/migrations/0014_v330_saved_launchtime_configs.py b/awx/main/migrations/0014_v330_saved_launchtime_configs.py
index fbd26eec1b..d9c7b105d9 100644
--- a/awx/main/migrations/0014_v330_saved_launchtime_configs.py
+++ b/awx/main/migrations/0014_v330_saved_launchtime_configs.py
@@ -20,7 +20,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='schedule',
name='char_prompts',
- field=awx.main.fields.JSONField(default={}, blank=True),
+ field=awx.main.fields.JSONField(default=dict, blank=True),
),
migrations.AddField(
model_name='schedule',
@@ -35,7 +35,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='schedule',
name='survey_passwords',
- field=awx.main.fields.JSONField(default={}, editable=False, blank=True),
+ field=awx.main.fields.JSONField(default=dict, editable=False, blank=True),
),
migrations.AddField(
model_name='workflowjobnode',
@@ -45,12 +45,12 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='workflowjobnode',
name='extra_data',
- field=awx.main.fields.JSONField(default={}, blank=True),
+ field=awx.main.fields.JSONField(default=dict, blank=True),
),
migrations.AddField(
model_name='workflowjobnode',
name='survey_passwords',
- field=awx.main.fields.JSONField(default={}, editable=False, blank=True),
+ field=awx.main.fields.JSONField(default=dict, editable=False, blank=True),
),
migrations.AddField(
model_name='workflowjobtemplatenode',
@@ -60,12 +60,12 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='workflowjobtemplatenode',
name='extra_data',
- field=awx.main.fields.JSONField(default={}, blank=True),
+ field=awx.main.fields.JSONField(default=dict, blank=True),
),
migrations.AddField(
model_name='workflowjobtemplatenode',
name='survey_passwords',
- field=awx.main.fields.JSONField(default={}, editable=False, blank=True),
+ field=awx.main.fields.JSONField(default=dict, editable=False, blank=True),
),
# Run data migration before removing the old credential field
migrations.RunPython(migration_utils.set_current_apps_for_migrations, migrations.RunPython.noop),
@@ -83,9 +83,9 @@ class Migration(migrations.Migration):
name='JobLaunchConfig',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('extra_data', awx.main.fields.JSONField(blank=True, default={})),
- ('survey_passwords', awx.main.fields.JSONField(blank=True, default={}, editable=False)),
- ('char_prompts', awx.main.fields.JSONField(blank=True, default={})),
+ ('extra_data', awx.main.fields.JSONField(blank=True, default=dict)),
+ ('survey_passwords', awx.main.fields.JSONField(blank=True, default=dict, editable=False)),
+ ('char_prompts', awx.main.fields.JSONField(blank=True, default=dict)),
('credentials', models.ManyToManyField(related_name='joblaunchconfigs', to='main.Credential')),
('inventory', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='joblaunchconfigs', to='main.Inventory')),
('job', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='launch_config', to='main.UnifiedJob')),
@@ -94,51 +94,51 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='workflowjobtemplate',
name='ask_variables_on_launch',
- field=awx.main.fields.AskForField(default=False),
+ field=awx.main.fields.AskForField(blank=True, default=False),
),
migrations.AlterField(
model_name='jobtemplate',
name='ask_credential_on_launch',
- field=awx.main.fields.AskForField(default=False),
+ field=awx.main.fields.AskForField(blank=True, default=False),
),
migrations.AlterField(
model_name='jobtemplate',
name='ask_diff_mode_on_launch',
- field=awx.main.fields.AskForField(default=False),
+ field=awx.main.fields.AskForField(blank=True, default=False),
),
migrations.AlterField(
model_name='jobtemplate',
name='ask_inventory_on_launch',
- field=awx.main.fields.AskForField(default=False),
+ field=awx.main.fields.AskForField(blank=True, default=False),
),
migrations.AlterField(
model_name='jobtemplate',
name='ask_job_type_on_launch',
- field=awx.main.fields.AskForField(default=False),
+ field=awx.main.fields.AskForField(blank=True, default=False),
),
migrations.AlterField(
model_name='jobtemplate',
name='ask_limit_on_launch',
- field=awx.main.fields.AskForField(default=False),
+ field=awx.main.fields.AskForField(blank=True, default=False),
),
migrations.AlterField(
model_name='jobtemplate',
name='ask_skip_tags_on_launch',
- field=awx.main.fields.AskForField(default=False),
+ field=awx.main.fields.AskForField(blank=True, default=False),
),
migrations.AlterField(
model_name='jobtemplate',
name='ask_tags_on_launch',
- field=awx.main.fields.AskForField(default=False),
+ field=awx.main.fields.AskForField(blank=True, default=False),
),
migrations.AlterField(
model_name='jobtemplate',
name='ask_variables_on_launch',
- field=awx.main.fields.AskForField(default=False),
+ field=awx.main.fields.AskForField(blank=True, default=False),
),
migrations.AlterField(
model_name='jobtemplate',
name='ask_verbosity_on_launch',
- field=awx.main.fields.AskForField(default=False),
+ field=awx.main.fields.AskForField(blank=True, default=False),
),
]
diff --git a/awx/main/migrations/0018_v330_add_additional_stdout_events.py b/awx/main/migrations/0018_v330_add_additional_stdout_events.py
index 80fdbe3bf7..33abaf0eee 100644
--- a/awx/main/migrations/0018_v330_add_additional_stdout_events.py
+++ b/awx/main/migrations/0018_v330_add_additional_stdout_events.py
@@ -20,7 +20,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(default=None, editable=False)),
('modified', models.DateTimeField(default=None, editable=False)),
- ('event_data', awx.main.fields.JSONField(blank=True, default={})),
+ ('event_data', awx.main.fields.JSONField(blank=True, default=dict)),
('uuid', models.CharField(default='', editable=False, max_length=1024)),
('counter', models.PositiveIntegerField(default=0, editable=False)),
('stdout', models.TextField(default='', editable=False)),
@@ -40,7 +40,7 @@ class Migration(migrations.Migration):
('created', models.DateTimeField(default=None, editable=False)),
('modified', models.DateTimeField(default=None, editable=False)),
('event', models.CharField(choices=[('runner_on_failed', 'Host Failed'), ('runner_on_ok', 'Host OK'), ('runner_on_error', 'Host Failure'), ('runner_on_skipped', 'Host Skipped'), ('runner_on_unreachable', 'Host Unreachable'), ('runner_on_no_hosts', 'No Hosts Remaining'), ('runner_on_async_poll', 'Host Polling'), ('runner_on_async_ok', 'Host Async OK'), ('runner_on_async_failed', 'Host Async Failure'), ('runner_item_on_ok', 'Item OK'), ('runner_item_on_failed', 'Item Failed'), ('runner_item_on_skipped', 'Item Skipped'), ('runner_retry', 'Host Retry'), ('runner_on_file_diff', 'File Difference'), ('playbook_on_start', 'Playbook Started'), ('playbook_on_notify', 'Running Handlers'), ('playbook_on_include', 'Including File'), ('playbook_on_no_hosts_matched', 'No Hosts Matched'), ('playbook_on_no_hosts_remaining', 'No Hosts Remaining'), ('playbook_on_task_start', 'Task Started'), ('playbook_on_vars_prompt', 'Variables Prompted'), ('playbook_on_setup', 'Gathering Facts'), ('playbook_on_import_for_host', 'internal: on Import for Host'), ('playbook_on_not_import_for_host', 'internal: on Not Import for Host'), ('playbook_on_play_start', 'Play Started'), ('playbook_on_stats', 'Playbook Complete'), ('debug', 'Debug'), ('verbose', 'Verbose'), ('deprecated', 'Deprecated'), ('warning', 'Warning'), ('system_warning', 'System Warning'), ('error', 'Error')], max_length=100)),
- ('event_data', awx.main.fields.JSONField(blank=True, default={})),
+ ('event_data', awx.main.fields.JSONField(blank=True, default=dict)),
('failed', models.BooleanField(default=False, editable=False)),
('changed', models.BooleanField(default=False, editable=False)),
('uuid', models.CharField(default='', editable=False, max_length=1024)),
@@ -65,7 +65,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(default=None, editable=False)),
('modified', models.DateTimeField(default=None, editable=False)),
- ('event_data', awx.main.fields.JSONField(blank=True, default={})),
+ ('event_data', awx.main.fields.JSONField(blank=True, default=dict)),
('uuid', models.CharField(default='', editable=False, max_length=1024)),
('counter', models.PositiveIntegerField(default=0, editable=False)),
('stdout', models.TextField(default='', editable=False)),
diff --git a/awx/main/migrations/0053_v340_workflow_inventory.py b/awx/main/migrations/0053_v340_workflow_inventory.py
index 285b4262fe..c519a27e25 100644
--- a/awx/main/migrations/0053_v340_workflow_inventory.py
+++ b/awx/main/migrations/0053_v340_workflow_inventory.py
@@ -17,7 +17,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='workflowjob',
name='char_prompts',
- field=awx.main.fields.JSONField(blank=True, default={}),
+ field=awx.main.fields.JSONField(blank=True, default=dict),
),
migrations.AddField(
model_name='workflowjob',
@@ -27,7 +27,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='workflowjobtemplate',
name='ask_inventory_on_launch',
- field=awx.main.fields.AskForField(default=False),
+ field=awx.main.fields.AskForField(blank=True, default=False),
),
migrations.AddField(
model_name='workflowjobtemplate',
diff --git a/awx/main/migrations/0067_v350_credential_plugins.py b/awx/main/migrations/0067_v350_credential_plugins.py
index e80e9f543b..32190b2bf2 100644
--- a/awx/main/migrations/0067_v350_credential_plugins.py
+++ b/awx/main/migrations/0067_v350_credential_plugins.py
@@ -34,7 +34,7 @@ class Migration(migrations.Migration):
('modified', models.DateTimeField(default=None, editable=False)),
('description', models.TextField(blank=True, default='')),
('input_field_name', models.CharField(max_length=1024)),
- ('metadata', awx.main.fields.DynamicCredentialInputField(blank=True, default={})),
+ ('metadata', awx.main.fields.DynamicCredentialInputField(blank=True, default=dict)),
('created_by', models.ForeignKey(default=None, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="{'class': 'credentialinputsource', 'model_name': 'credentialinputsource', 'app_label': 'main'}(class)s_created+", to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(default=None, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="{'class': 'credentialinputsource', 'model_name': 'credentialinputsource', 'app_label': 'main'}(class)s_modified+", to=settings.AUTH_USER_MODEL)),
('source_credential', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='target_input_sources', to='main.Credential')),
diff --git a/awx/main/migrations/_squashed_30.py b/awx/main/migrations/_squashed_30.py
index 910be80287..31ea44e885 100644
--- a/awx/main/migrations/_squashed_30.py
+++ b/awx/main/migrations/_squashed_30.py
@@ -30,7 +30,7 @@ SQUASHED_30 = {
migrations.AddField(
model_name='job',
name='survey_passwords',
- field=jsonfield.fields.JSONField(default={}, editable=False, blank=True),
+ field=jsonfield.fields.JSONField(default=dict, editable=False, blank=True),
),
],
'0031_v302_migrate_survey_passwords': [
diff --git a/awx/main/models/__init__.py b/awx/main/models/__init__.py
index 974aca40c8..01a44c58b7 100644
--- a/awx/main/models/__init__.py
+++ b/awx/main/models/__init__.py
@@ -62,24 +62,6 @@ from awx.main.models.oauth import ( # noqa
from oauth2_provider.models import Grant, RefreshToken # noqa -- needed django-oauth-toolkit model migrations
-
-# Monkeypatch Django serializer to ignore django-taggit fields (which break
-# the dumpdata command; see https://github.com/alex/django-taggit/issues/155).
-from django.core.serializers.python import Serializer as _PythonSerializer
-_original_handle_m2m_field = _PythonSerializer.handle_m2m_field
-
-
-def _new_handle_m2m_field(self, obj, field):
- try:
- field.rel.through._meta
- except AttributeError:
- return
- return _original_handle_m2m_field(self, obj, field)
-
-
-_PythonSerializer.handle_m2m_field = _new_handle_m2m_field
-
-
# Add custom methods to User model for permissions checks.
from django.contrib.auth.models import User # noqa
from awx.main.access import ( # noqa
@@ -158,7 +140,7 @@ User.add_to_class('is_system_auditor', user_is_system_auditor)
def user_is_in_enterprise_category(user, category):
- ret = (category,) in user.enterprise_auth.all().values_list('provider') and not user.has_usable_password()
+ ret = (category,) in user.enterprise_auth.values_list('provider') and not user.has_usable_password()
# NOTE: this if-else block ensures existing enterprise users are still able to
# log in. Remove it in a future release
if category == 'radius':
diff --git a/awx/main/models/credential/__init__.py b/awx/main/models/credential/__init__.py
index e99401ad7c..d34cac27e3 100644
--- a/awx/main/models/credential/__init__.py
+++ b/awx/main/models/credential/__init__.py
@@ -105,7 +105,7 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
)
inputs = CredentialInputField(
blank=True,
- default={},
+ default=dict,
help_text=_('Enter inputs using either JSON or YAML syntax. Use the '
'radio button to toggle between the two. Refer to the '
'Ansible Tower documentation for example syntax.')
@@ -343,14 +343,14 @@ class CredentialType(CommonModelNameNotUnique):
)
inputs = CredentialTypeInputField(
blank=True,
- default={},
+ default=dict,
help_text=_('Enter inputs using either JSON or YAML syntax. Use the '
'radio button to toggle between the two. Refer to the '
'Ansible Tower documentation for example syntax.')
)
injectors = CredentialTypeInjectorField(
blank=True,
- default={},
+ default=dict,
help_text=_('Enter injectors using either JSON or YAML syntax. Use the '
'radio button to toggle between the two. Refer to the '
'Ansible Tower documentation for example syntax.')
@@ -1117,7 +1117,7 @@ class CredentialInputSource(PrimordialModel):
)
metadata = DynamicCredentialInputField(
blank=True,
- default={}
+ default=dict
)
def clean_target_credential(self):
diff --git a/awx/main/models/events.py b/awx/main/models/events.py
index 5b424353eb..9a70501474 100644
--- a/awx/main/models/events.py
+++ b/awx/main/models/events.py
@@ -149,7 +149,7 @@ class BasePlaybookEvent(CreatedModifiedModel):
)
event_data = JSONField(
blank=True,
- default={},
+ default=dict,
)
failed = models.BooleanField(
default=False,
@@ -567,7 +567,7 @@ class BaseCommandEvent(CreatedModifiedModel):
event_data = JSONField(
blank=True,
- default={},
+ default=dict,
)
uuid = models.CharField(
max_length=1024,
diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py
index a32b2da052..eec1c5ae11 100644
--- a/awx/main/models/inventory.py
+++ b/awx/main/models/inventory.py
@@ -650,7 +650,7 @@ class Host(CommonModelNameNotUnique, RelatedJobsMixin):
)
ansible_facts = JSONBField(
blank=True,
- default={},
+ default=dict,
help_text=_('Arbitrary JSON structure of most recent ansible_facts, per-host.'),
)
ansible_facts_modified = models.DateTimeField(
diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py
index 59b26c66e5..12c691d195 100644
--- a/awx/main/models/jobs.py
+++ b/awx/main/models/jobs.py
@@ -485,7 +485,7 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana
)
artifacts = JSONField(
blank=True,
- default={},
+ default=dict,
editable=False,
)
scm_revision = models.CharField(
@@ -847,7 +847,7 @@ class LaunchTimeConfigBase(BaseModel):
# This is a solution to the nullable CharField problem, specific to prompting
char_prompts = JSONField(
blank=True,
- default={}
+ default=dict
)
def prompts_dict(self, display=False):
@@ -927,11 +927,11 @@ class LaunchTimeConfig(LaunchTimeConfigBase):
# Special case prompting fields, even more special than the other ones
extra_data = JSONField(
blank=True,
- default={}
+ default=dict
)
survey_passwords = prevent_search(JSONField(
blank=True,
- default={},
+ default=dict,
editable=False,
))
# Credentials needed for non-unified job / unified JT models
diff --git a/awx/main/models/mixins.py b/awx/main/models/mixins.py
index 437388664a..47176f2550 100644
--- a/awx/main/models/mixins.py
+++ b/awx/main/models/mixins.py
@@ -100,7 +100,7 @@ class SurveyJobTemplateMixin(models.Model):
)
survey_spec = prevent_search(JSONField(
blank=True,
- default={},
+ default=dict,
))
ask_variables_on_launch = AskForField(
blank=True,
@@ -360,7 +360,7 @@ class SurveyJobMixin(models.Model):
survey_passwords = prevent_search(JSONField(
blank=True,
- default={},
+ default=dict,
editable=False,
))
diff --git a/awx/main/models/unified_jobs.py b/awx/main/models/unified_jobs.py
index 438cc5f513..e0de362e8e 100644
--- a/awx/main/models/unified_jobs.py
+++ b/awx/main/models/unified_jobs.py
@@ -641,7 +641,7 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique
)
job_env = prevent_search(JSONField(
blank=True,
- default={},
+ default=dict,
editable=False,
))
job_explanation = models.TextField(
diff --git a/awx/main/models/workflow.py b/awx/main/models/workflow.py
index e5f36054ff..d413f05666 100644
--- a/awx/main/models/workflow.py
+++ b/awx/main/models/workflow.py
@@ -180,7 +180,7 @@ class WorkflowJobNode(WorkflowNodeBase):
)
ancestor_artifacts = JSONField(
blank=True,
- default={},
+ default=dict,
editable=False,
)
do_not_run = models.BooleanField(
diff --git a/awx/main/tests/functional/api/test_instance_group.py b/awx/main/tests/functional/api/test_instance_group.py
index e43bf5f2c4..381e6b349e 100644
--- a/awx/main/tests/functional/api/test_instance_group.py
+++ b/awx/main/tests/functional/api/test_instance_group.py
@@ -101,7 +101,7 @@ def test_instance_group_is_isolated(instance_group, isolated_instance_group):
assert not instance_group.is_isolated
assert isolated_instance_group.is_isolated
- isolated_instance_group.instances = []
+ isolated_instance_group.instances.set([])
assert isolated_instance_group.is_isolated
diff --git a/awx/main/tests/functional/commands/test_commands.py b/awx/main/tests/functional/commands/test_commands.py
index 471e5f1be6..078ca96f2d 100644
--- a/awx/main/tests/functional/commands/test_commands.py
+++ b/awx/main/tests/functional/commands/test_commands.py
@@ -12,7 +12,6 @@ def run_command(name, *args, **options):
command_runner = options.pop('command_runner', call_command)
stdin_fileobj = options.pop('stdin_fileobj', None)
options.setdefault('verbosity', 1)
- options.setdefault('interactive', False)
original_stdin = sys.stdin
original_stdout = sys.stdout
original_stderr = sys.stderr
diff --git a/awx/main/tests/functional/conftest.py b/awx/main/tests/functional/conftest.py
index 5a0efa37ae..c924034bdd 100644
--- a/awx/main/tests/functional/conftest.py
+++ b/awx/main/tests/functional/conftest.py
@@ -11,9 +11,9 @@ from django.urls import resolve
from django.contrib.auth.models import User
from django.core.serializers.json import DjangoJSONEncoder
from django.db.backends.sqlite3.base import SQLiteCursorWrapper
-from jsonbfield.fields import JSONField
# AWX
+from awx.main.fields import JSONBField
from awx.main.models.projects import Project
from awx.main.models.ha import Instance
@@ -737,7 +737,7 @@ def get_db_prep_save(self, value, connection, **kwargs):
@pytest.fixture
def monkeypatch_jsonbfield_get_db_prep_save(mocker):
- JSONField.get_db_prep_save = get_db_prep_save
+ JSONBField.get_db_prep_save = get_db_prep_save
@pytest.fixture
diff --git a/awx/main/tests/functional/models/test_schedule.py b/awx/main/tests/functional/models/test_schedule.py
index aee73e50eb..525fdd3022 100644
--- a/awx/main/tests/functional/models/test_schedule.py
+++ b/awx/main/tests/functional/models/test_schedule.py
@@ -457,4 +457,4 @@ def test_duplicate_name_within_template(job_template):
with pytest.raises(IntegrityError) as ierror:
s2.save()
- assert str(ierror.value) == "columns unified_job_template_id, name are not unique"
+ assert str(ierror.value) == "UNIQUE constraint failed: main_schedule.unified_job_template_id, main_schedule.name"
diff --git a/awx/main/tests/functional/models/test_workflow.py b/awx/main/tests/functional/models/test_workflow.py
index 3b61e6c29b..cc4c42ecfe 100644
--- a/awx/main/tests/functional/models/test_workflow.py
+++ b/awx/main/tests/functional/models/test_workflow.py
@@ -20,7 +20,6 @@ from django.test import TransactionTestCase
from django.core.exceptions import ValidationError
-@pytest.mark.django_db
class TestWorkflowDAGFunctional(TransactionTestCase):
def workflow_job(self, states=['new', 'new', 'new', 'new', 'new']):
"""
diff --git a/awx/main/tests/functional/task_management/test_capacity.py b/awx/main/tests/functional/task_management/test_capacity.py
index 7b7b7d7dc0..b3be1a3a77 100644
--- a/awx/main/tests/functional/task_management/test_capacity.py
+++ b/awx/main/tests/functional/task_management/test_capacity.py
@@ -1,5 +1,3 @@
-import pytest
-
from django.test import TransactionTestCase
from awx.main.models import (
@@ -8,7 +6,6 @@ from awx.main.models import (
)
-@pytest.mark.django_db
class TestCapacityMapping(TransactionTestCase):
def sample_cluster(self):
diff --git a/awx/main/tests/functional/test_jobs.py b/awx/main/tests/functional/test_jobs.py
index b04813b278..e847544931 100644
--- a/awx/main/tests/functional/test_jobs.py
+++ b/awx/main/tests/functional/test_jobs.py
@@ -42,7 +42,7 @@ def test_job_notification_data(inventory, machine_credential, project):
survey_passwords={"SSN": encrypted_str},
project=project,
)
- job.credentials = [machine_credential]
+ job.credentials.set([machine_credential])
notification_data = job.notification_data(block=0)
assert json.loads(notification_data['extra_vars'])['SSN'] == encrypted_str
diff --git a/awx/main/tests/functional/test_rbac_user.py b/awx/main/tests/functional/test_rbac_user.py
index 403767749f..c161a79c2f 100644
--- a/awx/main/tests/functional/test_rbac_user.py
+++ b/awx/main/tests/functional/test_rbac_user.py
@@ -7,7 +7,6 @@ from awx.main.access import UserAccess, RoleAccess, TeamAccess
from awx.main.models import User, Organization, Inventory
-@pytest.mark.django_db
class TestSysAuditorTransactional(TransactionTestCase):
def rando(self):
return User.objects.create(username='rando', password='rando', email='rando@com.com')
diff --git a/awx/main/tests/unit/api/test_generics.py b/awx/main/tests/unit/api/test_generics.py
index de7f8ab4c8..caac45bc3b 100644
--- a/awx/main/tests/unit/api/test_generics.py
+++ b/awx/main/tests/unit/api/test_generics.py
@@ -186,11 +186,8 @@ class TestResourceAccessList:
def mock_request(self):
return mock.MagicMock(
- user=mock.MagicMock(
- is_anonymous=mock.MagicMock(return_value=False),
- is_superuser=False
- ), method='GET')
-
+ user=mock.MagicMock(is_anonymous=False, is_superuser=False),
+ method='GET')
def mock_view(self, parent=None):
view = ResourceAccessList()
@@ -200,7 +197,6 @@ class TestResourceAccessList:
view.get_parent_object = lambda: parent
return view
-
def test_parent_access_check_failed(self, mocker, mock_organization):
mock_access = mocker.MagicMock(__name__='for logger', return_value=False)
with mocker.patch('awx.main.access.BaseAccess.can_read', mock_access):
@@ -208,7 +204,6 @@ class TestResourceAccessList:
self.mock_view(parent=mock_organization).check_permissions(self.mock_request())
mock_access.assert_called_once_with(mock_organization)
-
def test_parent_access_check_worked(self, mocker, mock_organization):
mock_access = mocker.MagicMock(__name__='for logger', return_value=True)
with mocker.patch('awx.main.access.BaseAccess.can_read', mock_access):
diff --git a/awx/main/tests/unit/conftest.py b/awx/main/tests/unit/conftest.py
index 729f14ecaf..26b7049477 100644
--- a/awx/main/tests/unit/conftest.py
+++ b/awx/main/tests/unit/conftest.py
@@ -6,7 +6,7 @@ from unittest.mock import PropertyMock
from awx.api.urls import urlpatterns as api_patterns
# Django
-from django.urls import RegexURLResolver, RegexURLPattern
+from django.urls import URLResolver, URLPattern
@pytest.fixture(autouse=True)
@@ -20,24 +20,24 @@ def all_views():
'''
returns a set of all views in the app
'''
- patterns = set([])
- url_views = set([])
+ patterns = set()
+ url_views = set()
# Add recursive URL patterns
unprocessed = set(api_patterns)
while unprocessed:
to_process = unprocessed.copy()
- unprocessed = set([])
+ unprocessed = set()
for pattern in to_process:
if hasattr(pattern, 'lookup_str') and not pattern.lookup_str.startswith('awx.api'):
continue
patterns.add(pattern)
- if isinstance(pattern, RegexURLResolver):
+ if isinstance(pattern, URLResolver):
for sub_pattern in pattern.url_patterns:
if sub_pattern not in patterns:
unprocessed.add(sub_pattern)
# Get view classes
for pattern in patterns:
- if isinstance(pattern, RegexURLPattern) and hasattr(pattern.callback, 'view_class'):
+ if isinstance(pattern, URLPattern) and hasattr(pattern.callback, 'view_class'):
url_views.add(pattern.callback.view_class)
return url_views
diff --git a/awx/main/tests/unit/models/test_events.py b/awx/main/tests/unit/models/test_events.py
index 734d38f449..79d23a3757 100644
--- a/awx/main/tests/unit/models/test_events.py
+++ b/awx/main/tests/unit/models/test_events.py
@@ -57,5 +57,5 @@ def test_really_long_event_fields(field):
})
manager.create.assert_called_with(**{
'job_id': 123,
- 'event_data': {field: 'X' * 1021 + '...'}
+ 'event_data': {field: 'X' * 1023 + '…'}
})
diff --git a/awx/main/utils/named_url_graph.py b/awx/main/utils/named_url_graph.py
index d21da9e7a1..f17c503d4c 100644
--- a/awx/main/utils/named_url_graph.py
+++ b/awx/main/utils/named_url_graph.py
@@ -208,7 +208,7 @@ def _check_unique_together_fields(model, ut):
field = model._meta.get_field(field_name)
if field_name == 'name':
has_name = True
- elif type(field) == models.ForeignKey and field.rel.to != model:
+ elif type(field) == models.ForeignKey and field.related_model != model:
fk_names.append(field_name)
elif issubclass(type(field), models.CharField) and field.choices:
fields.append(field_name)
@@ -256,7 +256,7 @@ def _dfs(configuration, model, graph, dead_ends, new_deadends, parents):
fields, fk_names = configuration[model][0][:], configuration[model][1][:]
adj_list = []
for fk_name in fk_names:
- next_model = model._meta.get_field(fk_name).rel.to
+ next_model = model._meta.get_field(fk_name).related_model
if issubclass(next_model, ContentType):
continue
if next_model not in configuration or\
diff --git a/awx/main/views.py b/awx/main/views.py
index 13230d0846..1947bd001c 100644
--- a/awx/main/views.py
+++ b/awx/main/views.py
@@ -60,7 +60,7 @@ def handle_error(request, status=404, **kwargs):
return render(request, 'error.html', kwargs, status=status)
-def handle_400(request):
+def handle_400(request, exception):
kwargs = {
'name': _('Bad Request'),
'content': _('The request could not be understood by the server.'),
@@ -68,7 +68,7 @@ def handle_400(request):
return handle_error(request, 400, **kwargs)
-def handle_403(request):
+def handle_403(request, exception):
kwargs = {
'name': _('Forbidden'),
'content': _('You don\'t have permission to access the requested resource.'),
@@ -76,7 +76,7 @@ def handle_403(request):
return handle_error(request, 403, **kwargs)
-def handle_404(request):
+def handle_404(request, exception):
kwargs = {
'name': _('Not Found'),
'content': _('The requested resource could not be found.'),
diff --git a/awx/sso/backends.py b/awx/sso/backends.py
index df9df8a57e..35990e4622 100644
--- a/awx/sso/backends.py
+++ b/awx/sso/backends.py
@@ -13,6 +13,7 @@ from django.dispatch import receiver
from django.contrib.auth.models import User
from django.conf import settings as django_settings
from django.core.signals import setting_changed
+from django.utils.encoding import force_text
# django-auth-ldap
from django_auth_ldap.backend import LDAPSettings as BaseLDAPSettings
@@ -98,7 +99,7 @@ class LDAPBackend(BaseLDAPBackend):
settings = property(_get_settings, _set_settings)
- def authenticate(self, username, password):
+ def authenticate(self, request, username, password):
if self.settings.START_TLS and ldap.OPT_X_TLS_REQUIRE_CERT in self.settings.CONNECTION_OPTIONS:
# with python-ldap, if you want to set connection-specific TLS
# parameters, you must also specify OPT_X_TLS_NEWCTX = 0
@@ -124,7 +125,7 @@ class LDAPBackend(BaseLDAPBackend):
raise ImproperlyConfigured(
"{} must be an {} instance.".format(setting_name, type_)
)
- return super(LDAPBackend, self).authenticate(None, username, password)
+ return super(LDAPBackend, self).authenticate(request, username, password)
except Exception:
logger.exception("Encountered an error authenticating to LDAP")
return None
@@ -179,7 +180,7 @@ def _decorate_enterprise_user(user, provider):
def _get_or_set_enterprise_user(username, password, provider):
created = False
try:
- user = User.objects.all().prefetch_related('enterprise_auth').get(username=username)
+ user = User.objects.prefetch_related('enterprise_auth').get(username=username)
except User.DoesNotExist:
user = User(username=username)
enterprise_auth = _decorate_enterprise_user(user, provider)
@@ -196,10 +197,10 @@ class RADIUSBackend(BaseRADIUSBackend):
Custom Radius backend to verify license status
'''
- def authenticate(self, username, password):
+ def authenticate(self, request, username, password):
if not django_settings.RADIUS_SERVER:
return None
- return super(RADIUSBackend, self).authenticate(None, username, password)
+ return super(RADIUSBackend, self).authenticate(request, username, password)
def get_user(self, user_id):
if not django_settings.RADIUS_SERVER:
@@ -209,7 +210,7 @@ class RADIUSBackend(BaseRADIUSBackend):
return user
def get_django_user(self, username, password=None):
- return _get_or_set_enterprise_user(username, password, 'radius')
+ return _get_or_set_enterprise_user(force_text(username), force_text(password), 'radius')
class TACACSPlusBackend(object):
@@ -217,7 +218,7 @@ class TACACSPlusBackend(object):
Custom TACACS+ auth backend for AWX
'''
- def authenticate(self, username, password):
+ def authenticate(self, request, username, password):
if not django_settings.TACACSPLUS_HOST:
return None
try:
@@ -284,13 +285,13 @@ class SAMLAuth(BaseSAMLAuth):
idp_config = self.setting('ENABLED_IDPS')[idp_name]
return TowerSAMLIdentityProvider(idp_name, **idp_config)
- def authenticate(self, *args, **kwargs):
+ def authenticate(self, request, *args, **kwargs):
if not all([django_settings.SOCIAL_AUTH_SAML_SP_ENTITY_ID, django_settings.SOCIAL_AUTH_SAML_SP_PUBLIC_CERT,
django_settings.SOCIAL_AUTH_SAML_SP_PRIVATE_KEY, django_settings.SOCIAL_AUTH_SAML_ORG_INFO,
django_settings.SOCIAL_AUTH_SAML_TECHNICAL_CONTACT, django_settings.SOCIAL_AUTH_SAML_SUPPORT_CONTACT,
django_settings.SOCIAL_AUTH_SAML_ENABLED_IDPS]):
return None
- user = super(SAMLAuth, self).authenticate(*args, **kwargs)
+ user = super(SAMLAuth, self).authenticate(request, *args, **kwargs)
# Comes from https://github.com/omab/python-social-auth/blob/v0.2.21/social/backends/base.py#L91
if getattr(user, 'is_new', False):
_decorate_enterprise_user(user, 'saml')
@@ -307,7 +308,7 @@ class SAMLAuth(BaseSAMLAuth):
return super(SAMLAuth, self).get_user(user_id)
-def _update_m2m_from_groups(user, ldap_user, rel, opts, remove=True):
+def _update_m2m_from_groups(user, ldap_user, related, opts, remove=True):
'''
Hepler function to update m2m relationship based on LDAP group membership.
'''
@@ -328,10 +329,10 @@ def _update_m2m_from_groups(user, ldap_user, rel, opts, remove=True):
should_add = True
if should_add:
user.save()
- rel.add(user)
- elif remove and user in rel.all():
+ related.add(user)
+ elif remove and user in related.all():
user.save()
- rel.remove(user)
+ related.remove(user)
@receiver(populate_user, dispatch_uid='populate-ldap-user')
diff --git a/awx/sso/middleware.py b/awx/sso/middleware.py
index 4edd4c4a2e..5ed1e5a9e2 100644
--- a/awx/sso/middleware.py
+++ b/awx/sso/middleware.py
@@ -39,7 +39,7 @@ class SocialAuthMiddleware(SocialAuthExceptionMiddleware):
request.successful_authenticator = None
if not request.path.startswith('/sso/') and 'migrations_notran' not in request.path:
- if request.user and request.user.is_authenticated():
+ if request.user and request.user.is_authenticated:
# The rest of the code base rely hevily on type/inheritance checks,
# LazyObject sent from Django auth middleware can be buggy if not
# converted back to its original object.
diff --git a/awx/sso/migrations/0001_initial.py b/awx/sso/migrations/0001_initial.py
index 540215cf7f..69bc5ec7c7 100644
--- a/awx/sso/migrations/0001_initial.py
+++ b/awx/sso/migrations/0001_initial.py
@@ -17,7 +17,7 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('provider', models.CharField(max_length=32, choices=[(b'radius', 'RADIUS'), (b'tacacs+', 'TACACS+')])),
- ('user', models.ForeignKey(related_name='enterprise_auth', to=settings.AUTH_USER_MODEL)),
+ ('user', models.ForeignKey(related_name='enterprise_auth', on_delete=models.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.AlterUniqueTogether(
diff --git a/awx/sso/pipeline.py b/awx/sso/pipeline.py
index 1343f2e75e..635787ef7a 100644
--- a/awx/sso/pipeline.py
+++ b/awx/sso/pipeline.py
@@ -50,7 +50,7 @@ def prevent_inactive_login(backend, details, user=None, *args, **kwargs):
raise AuthInactive(backend)
-def _update_m2m_from_expression(user, rel, expr, remove=True):
+def _update_m2m_from_expression(user, related, expr, remove=True):
'''
Helper function to update m2m relationship based on user matching one or
more expressions.
@@ -73,12 +73,12 @@ def _update_m2m_from_expression(user, rel, expr, remove=True):
if ex.match(user.username) or ex.match(user.email):
should_add = True
if should_add:
- rel.add(user)
+ related.add(user)
elif remove:
- rel.remove(user)
+ related.remove(user)
-def _update_org_from_attr(user, rel, attr, remove, remove_admins):
+def _update_org_from_attr(user, related, attr, remove, remove_admins):
from awx.main.models import Organization
org_ids = []
@@ -87,7 +87,7 @@ def _update_org_from_attr(user, rel, attr, remove, remove_admins):
org = Organization.objects.get_or_create(name=org_name)[0]
org_ids.append(org.id)
- getattr(org, rel).members.add(user)
+ getattr(org, related).members.add(user)
if remove:
[o.member_role.members.remove(user) for o in
diff --git a/awx/sso/tests/conftest.py b/awx/sso/tests/conftest.py
index aa31bb0453..f94b1c528f 100644
--- a/awx/sso/tests/conftest.py
+++ b/awx/sso/tests/conftest.py
@@ -27,6 +27,7 @@ def existing_tacacsplus_user():
user = User.objects.get(username="foo")
except User.DoesNotExist:
user = User(username="foo")
+ user.set_unusable_password()
user.save()
enterprise_auth = UserEnterpriseAuth(user=user, provider='tacacs+')
enterprise_auth.save()
diff --git a/awx/sso/tests/functional/test_get_or_set_enterprise_user.py b/awx/sso/tests/functional/test_get_or_set_enterprise_user.py
index b15c0b4e9a..9844c17295 100644
--- a/awx/sso/tests/functional/test_get_or_set_enterprise_user.py
+++ b/awx/sso/tests/functional/test_get_or_set_enterprise_user.py
@@ -8,8 +8,11 @@ from awx.sso.backends import _get_or_set_enterprise_user
@pytest.mark.django_db
def test_fetch_user_if_exist(existing_tacacsplus_user):
- new_user = _get_or_set_enterprise_user("foo", "password", "tacacs+")
- assert new_user == existing_tacacsplus_user
+ with mock.patch('awx.sso.backends.logger') as mocked_logger:
+ new_user = _get_or_set_enterprise_user("foo", "password", "tacacs+")
+ mocked_logger.debug.assert_not_called()
+ mocked_logger.warn.assert_not_called()
+ assert new_user == existing_tacacsplus_user
@pytest.mark.django_db
diff --git a/awx/sso/tests/unit/test_tacacsplus.py b/awx/sso/tests/unit/test_tacacsplus.py
index c10cbd317e..e475694d63 100644
--- a/awx/sso/tests/unit/test_tacacsplus.py
+++ b/awx/sso/tests/unit/test_tacacsplus.py
@@ -4,7 +4,7 @@ from unittest import mock
def test_empty_host_fails_auth(tacacsplus_backend):
with mock.patch('awx.sso.backends.django_settings') as settings:
settings.TACACSPLUS_HOST = ''
- ret_user = tacacsplus_backend.authenticate(u"user", u"pass")
+ ret_user = tacacsplus_backend.authenticate(None, u"user", u"pass")
assert ret_user is None
@@ -16,7 +16,7 @@ def test_client_raises_exception(tacacsplus_backend):
mock.patch('tacacs_plus.TACACSClient', return_value=client):
settings.TACACSPLUS_HOST = 'localhost'
settings.TACACSPLUS_AUTH_PROTOCOL = 'ascii'
- ret_user = tacacsplus_backend.authenticate(u"user", u"pass")
+ ret_user = tacacsplus_backend.authenticate(None, u"user", u"pass")
assert ret_user is None
logger.exception.assert_called_once_with(
"TACACS+ Authentication Error: foo"
@@ -32,7 +32,7 @@ def test_client_return_invalid_fails_auth(tacacsplus_backend):
mock.patch('tacacs_plus.TACACSClient', return_value=client):
settings.TACACSPLUS_HOST = 'localhost'
settings.TACACSPLUS_AUTH_PROTOCOL = 'ascii'
- ret_user = tacacsplus_backend.authenticate(u"user", u"pass")
+ ret_user = tacacsplus_backend.authenticate(None, u"user", u"pass")
assert ret_user is None
@@ -48,5 +48,5 @@ def test_client_return_valid_passes_auth(tacacsplus_backend):
mock.patch('awx.sso.backends._get_or_set_enterprise_user', return_value=user):
settings.TACACSPLUS_HOST = 'localhost'
settings.TACACSPLUS_AUTH_PROTOCOL = 'ascii'
- ret_user = tacacsplus_backend.authenticate(u"user", u"pass")
+ ret_user = tacacsplus_backend.authenticate(None, u"user", u"pass")
assert ret_user == user
diff --git a/awx/sso/views.py b/awx/sso/views.py
index 9eb0c33a95..fa248f634f 100644
--- a/awx/sso/views.py
+++ b/awx/sso/views.py
@@ -40,7 +40,7 @@ class CompleteView(BaseRedirectView):
def dispatch(self, request, *args, **kwargs):
response = super(CompleteView, self).dispatch(request, *args, **kwargs)
- if self.request.user and self.request.user.is_authenticated():
+ if self.request.user and self.request.user.is_authenticated:
logger.info(smart_text(u"User {} logged in".format(self.request.user.username)))
response.set_cookie('userLoggedIn', 'true')
current_user = UserSerializer(self.request.user)
diff --git a/awx/templates/error.html b/awx/templates/error.html
index 69fade819e..81e54fe434 100644
--- a/awx/templates/error.html
+++ b/awx/templates/error.html
@@ -1,5 +1,5 @@
{% extends "rest_framework/api.html" %}
-{% load i18n staticfiles %}
+{% load i18n static %}
{% block title %}{{ name }} &middot; {% trans 'AWX' %}{% endblock %}
diff --git a/awx/templates/rest_framework/login.html b/awx/templates/rest_framework/login.html
index c9c5d64030..343d8a6ce0 100644
--- a/awx/templates/rest_framework/login.html
+++ b/awx/templates/rest_framework/login.html
@@ -1,6 +1,6 @@
{# Partial copy of login_base.html from rest_framework with AWX change. #}
{% extends 'rest_framework/api.html' %}
-{% load i18n staticfiles %}
+{% load i18n static %}
{% block breadcrumbs %}
{% endblock %}
diff --git a/awx/wsgi.py b/awx/wsgi.py
index 13f8b08198..6d155ab6c3 100644
--- a/awx/wsgi.py
+++ b/awx/wsgi.py
@@ -40,12 +40,5 @@ if social_django.__version__ != '2.1.0':
still works".format(social_django.__version__))
-if not django.__version__.startswith('1.'):
- raise RuntimeError("Django version other than 1.XX detected {}. \
- Inherit from WSGIHandler to support short-circuit Django Middleware. \
- This is known to work for Django 1.XX and may not work with other, \
- even minor, versions.".format(django.__version__))
-
-
# Return the default Django WSGI application.
application = get_wsgi_application()
diff --git a/docs/licenses/django-jsonbfield.txt b/docs/licenses/django-jsonbfield.txt
deleted file mode 100644
index 028fb0f561..0000000000
--- a/docs/licenses/django-jsonbfield.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-
-Copyright (c) Django Software Foundation and individual contributors.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
- 1. Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- 3. Neither the name of Django nor the names of its contributors may be used
- to endorse or promote products derived from this software without
- specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/docs/licenses/sqlparse.txt b/docs/licenses/sqlparse.txt
new file mode 100644
index 0000000000..de414c5579
--- /dev/null
+++ b/docs/licenses/sqlparse.txt
@@ -0,0 +1,25 @@
+Copyright (c) 2016, Andi Albrecht <albrecht.andi@gmail.com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name of the authors nor the names of its contributors may be
+ used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/requirements/requirements.in b/requirements/requirements.in
index 3635efa7b5..5adae509d0 100644
--- a/requirements/requirements.in
+++ b/requirements/requirements.in
@@ -7,12 +7,12 @@ channels==1.1.8
celery==4.3.0
daphne==1.3.0 # Last before backwards-incompatible channels 2 upgrade
twisted[tls]>=17.1 # from daphne, see https://github.com/django/daphne/pull/257
-Django==1.11.20
+Django==2.2.2
django-auth-ldap==1.7.0
django-cors-headers==2.4.0
django-crum==0.7.2
django-extensions==2.0.0
-django-jsonfield==1.0.1
+django-jsonfield==1.2.0
django-oauth-toolkit==1.1.3
django-polymorphic==2.0.2
django-pglocks==1.0.2
diff --git a/requirements/requirements.txt b/requirements/requirements.txt
index 56adbd6a82..24ae822d0f 100644
--- a/requirements/requirements.txt
+++ b/requirements/requirements.txt
@@ -27,7 +27,7 @@ django-auth-ldap==1.7.0
django-cors-headers==2.4.0
django-crum==0.7.2
django-extensions==2.0.0
-django-jsonfield==1.0.1
+django-jsonfield==1.2.0
django-oauth-toolkit==1.1.3
django-pglocks==1.0.2
django-polymorphic==2.0.2
@@ -35,7 +35,7 @@ django-radius==1.3.3
django-solo==1.1.3
django-split-settings==0.3.0
django-taggit==0.22.2
-django==1.11.20
+django==2.2.2
djangorestframework-yaml==1.0.3
djangorestframework==3.9.4
@@ -102,6 +102,7 @@ six==1.12.0 # via ansible-runner, asgi-amqp, asgiref, autobahn, au
slackclient==1.1.2
social-auth-app-django==2.1.0
social-auth-core==3.0.0
+sqlparse==0.3.0 # via django
tacacs_plus==1.0
tempora==1.14.1 # via irc, jaraco.logging
twilio==6.10.4
diff --git a/requirements/requirements_git.txt b/requirements/requirements_git.txt
index 46953643f7..9ce11ac140 100644
--- a/requirements/requirements_git.txt
+++ b/requirements/requirements_git.txt
@@ -1,3 +1,2 @@
git+https://github.com/ansible/ansiconv.git@tower_1.0.0#egg=ansiconv
git+https://github.com/ansible/django-qsstats-magic.git@py3#egg=django-qsstats-magic
-git+https://github.com/ansible/django-jsonbfield@fix-sqlite_serialization#egg=jsonbfield
diff --git a/tools/docker-compose/Dockerfile b/tools/docker-compose/Dockerfile
index 738daa305d..0d7671fe23 100644
--- a/tools/docker-compose/Dockerfile
+++ b/tools/docker-compose/Dockerfile
@@ -1,11 +1,9 @@
-FROM centos:7
+FROM fedora:27
ARG UID=0
-RUN yum -y update && yum -y install epel-release
-
# sync with installer/roles/image_build/templates/Dockerfile.j2
-RUN yum -y install acl \
+RUN dnf -y install acl \
alsa-lib \
ansible \
atk \
@@ -14,12 +12,15 @@ RUN yum -y install acl \
curl \
cyrus-sasl \
cyrus-sasl-devel \
+ findutils \
gcc \
gcc-c++ \
GConf2 \
git \
+ glibc-locale-source \
gtk3 \
ipa-gothic-fonts \
+ iproute \
krb5-devel \
krb5-libs \
krb5-workstation \
@@ -45,16 +46,16 @@ RUN yum -y install acl \
nodejs \
openldap-devel \
openssh-server \
+ postgresql-contrib \
postgresql-devel \
python-devel \
python-pip \
python-psutil \
python-psycopg2 \
python-setuptools \
- python36-devel \
- python36-setuptools \
+ python3-devel \
+ python3-setuptools \
rsync \
- setools-libs \
subversion \
sudo \
swig \