From 8dc5e7725c086704c5c31cb5623f0078b14da3ab Mon Sep 17 00:00:00 2001 From: Chris Church Date: Tue, 15 Nov 2016 00:22:55 -0500 Subject: [PATCH 1/2] Fix configure Tower in Tower to work with updated django-jsonfield. --- .../commands/migrate_to_database_settings.py | 3 -- .../0002_v310_copy_tower_settings.py | 5 ---- awx/conf/models.py | 2 -- awx/conf/settings.py | 29 +++++++++---------- awx/conf/views.py | 4 --- awx/main/fields.py | 2 -- awx/sso/fields.py | 6 ++++ 7 files changed, 20 insertions(+), 31 deletions(-) diff --git a/awx/conf/management/commands/migrate_to_database_settings.py b/awx/conf/management/commands/migrate_to_database_settings.py index c936e223c2..618c8cf725 100644 --- a/awx/conf/management/commands/migrate_to_database_settings.py +++ b/awx/conf/management/commands/migrate_to_database_settings.py @@ -314,9 +314,6 @@ class Command(BaseCommand): self.stdout.write(' No settings to migrate!') for name, db_value in to_migrate.items(): display_value = json.dumps(db_value, indent=4) - # Always encode "raw" strings as JSON. - if isinstance(db_value, basestring): - db_value = json.dumps(db_value) setting = Setting.objects.filter(key=name, user__isnull=True).order_by('pk').first() action = 'No Change' if not setting: diff --git a/awx/conf/migrations/0002_v310_copy_tower_settings.py b/awx/conf/migrations/0002_v310_copy_tower_settings.py index 2fbdd60477..4493007f70 100644 --- a/awx/conf/migrations/0002_v310_copy_tower_settings.py +++ b/awx/conf/migrations/0002_v310_copy_tower_settings.py @@ -14,11 +14,6 @@ def copy_tower_settings(apps, schema_editor): # LICENSE is stored as a string; convert it to a dict. if tower_setting.key == 'LICENSE': value = json.loads(value) - # Anything else (e.g. TOWER_URL_BASE) that is stored as a string - # needs to be converted to a JSON-encoded string to work with the - # JSON field. - elif tower_setting.value_type == 'string': - value = json.dumps(value) setting, created = Setting.objects.get_or_create( key=tower_setting.key, user=tower_setting.user, diff --git a/awx/conf/models.py b/awx/conf/models.py index 8a018cbd0b..2a32922cbd 100644 --- a/awx/conf/models.py +++ b/awx/conf/models.py @@ -21,8 +21,6 @@ class Setting(CreatedModifiedModel): ) value = JSONField( null=True, - # FIXME: Enable when we upgrade to JSONField with support: - # load_kwargs={'object_pairs_hook': collections.OrderedDict}, ) user = models.ForeignKey( 'auth.User', diff --git a/awx/conf/settings.py b/awx/conf/settings.py index 81159c80a9..55133a33c3 100644 --- a/awx/conf/settings.py +++ b/awx/conf/settings.py @@ -1,6 +1,5 @@ # Python import contextlib -import json import logging import threading import time @@ -142,16 +141,18 @@ class SettingsWrapper(UserSettingsHolder): def _get_local(self, name): self._preload_cache() cache_key = Setting.get_cache_key(name) - value = cache.get(cache_key, empty) - logger.debug('cache get(%r, %r) -> %r', cache_key, empty, value) - if value == SETTING_CACHE_NOTSET: + cache_value = cache.get(cache_key, empty) + logger.debug('cache get(%r, %r) -> %r', cache_key, empty, cache_value) + if cache_value == SETTING_CACHE_NOTSET: value = empty - elif value == SETTING_CACHE_NONE: + elif cache_value == SETTING_CACHE_NONE: value = None - elif value == SETTING_CACHE_EMPTY_LIST: + elif cache_value == SETTING_CACHE_EMPTY_LIST: value = [] - elif value == SETTING_CACHE_EMPTY_DICT: + elif cache_value == SETTING_CACHE_EMPTY_DICT: value = {} + else: + value = cache_value field = settings_registry.get_setting_field(name) if value is empty: setting = None @@ -159,9 +160,6 @@ class SettingsWrapper(UserSettingsHolder): setting = Setting.objects.filter(key=name, user__isnull=True).order_by('pk').first() if setting: value = setting.value - # If None implies not set, convert when reading the value. - if value is None and SETTING_CACHE_NOTSET == SETTING_CACHE_NONE: - value = SETTING_CACHE_NOTSET else: value = SETTING_CACHE_NOTSET if SETTING_CACHE_DEFAULTS: @@ -169,8 +167,12 @@ class SettingsWrapper(UserSettingsHolder): value = field.get_default() except SkipField: pass - logger.debug('cache set(%r, %r, %r)', cache_key, self._get_cache_value(value), SETTING_CACHE_TIMEOUT) - cache.set(cache_key, self._get_cache_value(value), SETTING_CACHE_TIMEOUT) + # If None implies not set, convert when reading the value. + if value is None and SETTING_CACHE_NOTSET == SETTING_CACHE_NONE: + value = SETTING_CACHE_NOTSET + if cache_value != value: + logger.debug('cache set(%r, %r, %r)', cache_key, self._get_cache_value(value), SETTING_CACHE_TIMEOUT) + cache.set(cache_key, self._get_cache_value(value), SETTING_CACHE_TIMEOUT) if value == SETTING_CACHE_NOTSET and not SETTING_CACHE_DEFAULTS: try: value = field.get_default() @@ -218,9 +220,6 @@ class SettingsWrapper(UserSettingsHolder): logger.exception('Unable to assign value "%r" to setting "%s".', value, name, exc_info=True) raise e - # Always encode "raw" strings as JSON. - if isinstance(db_value, basestring): - db_value = json.dumps(db_value) setting = Setting.objects.filter(key=name, user__isnull=True).order_by('pk').first() if not setting: setting = Setting.objects.create(key=name, user=None, value=db_value) diff --git a/awx/conf/views.py b/awx/conf/views.py index f6af0329d2..f587e5ee6f 100644 --- a/awx/conf/views.py +++ b/awx/conf/views.py @@ -3,7 +3,6 @@ # Python import collections -import json import sys # Django @@ -100,9 +99,6 @@ class SettingSingletonDetail(RetrieveUpdateDestroyAPIView): if key == 'LICENSE': continue setattr(serializer.instance, key, value) - # Always encode "raw" strings as JSON. - if isinstance(value, basestring): - value = json.dumps(value) setting = settings_qs.filter(key=key).order_by('pk').first() if not setting: setting = Setting.objects.create(key=key, user=user, value=value) diff --git a/awx/main/fields.py b/awx/main/fields.py index cc9ff5303d..6778e304d0 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -33,8 +33,6 @@ __all__ = ['AutoOneToOneField', 'ImplicitRoleField', 'JSONField'] class JSONField(upstream_JSONField): def from_db_value(self, value, expression, connection, context): - if value in ['', None]: - return {} return super(JSONField, self).from_db_value(value, expression, connection, context) # Based on AutoOneToOneField from django-annoying: diff --git a/awx/sso/fields.py b/awx/sso/fields.py index a0d472756e..750516a2c8 100644 --- a/awx/sso/fields.py +++ b/awx/sso/fields.py @@ -426,6 +426,12 @@ class LDAPTeamMapField(fields.DictField): class RADIUSSecretField(fields.CharField): + def run_validation(self, data=empty): + value = super(RADIUSSecretField, self).run_validation(data) + if isinstance(value, unicode): + value = value.encode('utf-8') + return value + def to_internal_value(self, value): value = super(RADIUSSecretField, self).to_internal_value(value) if isinstance(value, unicode): From 3eb6f55a80038fa823ae7a43711258d9fd972dde Mon Sep 17 00:00:00 2001 From: Chris Church Date: Tue, 15 Nov 2016 10:21:34 -0500 Subject: [PATCH 2/2] Default JSONField to empty dict if field does not allow None. --- awx/main/fields.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/awx/main/fields.py b/awx/main/fields.py index 6778e304d0..91f59bab8a 100644 --- a/awx/main/fields.py +++ b/awx/main/fields.py @@ -33,6 +33,8 @@ __all__ = ['AutoOneToOneField', 'ImplicitRoleField', 'JSONField'] class JSONField(upstream_JSONField): def from_db_value(self, value, expression, connection, context): + if value in {'', None} and not self.null: + return {} return super(JSONField, self).from_db_value(value, expression, connection, context) # Based on AutoOneToOneField from django-annoying: