Merge pull request #5143 from ryanpetrello/fix-4876

add a field to OPTIONS that tells if the setting came from a .py file
This commit is contained in:
Ryan Petrello 2017-02-02 13:40:27 -05:00 committed by GitHub
commit 1d8b8a6d11
5 changed files with 61 additions and 20 deletions

View File

@ -32,6 +32,7 @@ class Metadata(metadata.SimpleMetadata):
'min_length', 'max_length',
'min_value', 'max_value',
'category', 'category_slug',
'defined_in_file'
]
for attr in text_attrs:
@ -156,6 +157,10 @@ class Metadata(metadata.SimpleMetadata):
# For PUT/POST methods, remove read-only fields.
if method in ('PUT', 'POST'):
# This value should always be False for PUT/POST, so don't
# show it (file-based read-only settings can't be updated)
meta.pop('defined_in_file', False)
if meta.pop('read_only', False):
actions[method].pop(field)

View File

@ -116,6 +116,7 @@ class SettingsRegistry(object):
placeholder = field_kwargs.pop('placeholder', empty)
feature_required = field_kwargs.pop('feature_required', empty)
encrypted = bool(field_kwargs.pop('encrypted', False))
defined_in_file = bool(field_kwargs.pop('defined_in_file', False))
if getattr(field_kwargs.get('child', None), 'source', None) is not None:
field_kwargs['child'].source = None
field_instance = field_class(**field_kwargs)
@ -126,6 +127,13 @@ class SettingsRegistry(object):
field_instance.placeholder = placeholder
if feature_required is not empty:
field_instance.feature_required = feature_required
field_instance.defined_in_file = defined_in_file
if field_instance.defined_in_file:
field_instance.help_text = (
str(_('This value has been set manually in a settings file.')) +
'\n\n' +
str(field_instance.help_text)
)
field_instance.encrypted = encrypted
original_field_instance = field_instance
if field_class != original_field_class:

View File

@ -205,6 +205,7 @@ class SettingsWrapper(UserSettingsHolder):
if file_default != init_default and file_default is not None:
logger.warning('Setting %s has been marked read-only!', key)
self.registry._registry[key]['read_only'] = True
self.registry._registry[key]['defined_in_file'] = True
self.__dict__['_awx_conf_init_readonly'] = True
# If local preload timer has expired, check to see if another process
# has already preloaded the cache and skip preloading if so.

View File

@ -25,11 +25,11 @@ def reg(request):
settings = LazySettings()
registry = SettingsRegistry(settings)
# @pytest.mark.readonly can be used to mark specific setting values as
# "read-only". This is analogous to manually specifying a setting on the
# filesystem (e.g., in a local_settings.py in development, or in
# /etc/tower/conf.d/<something>.py)
defaults = request.node.get_marker('readonly')
# @pytest.mark.defined_in_file can be used to mark specific setting values
# as "defined in a settings file". This is analogous to manually
# specifying a setting on the filesystem (e.g., in a local_settings.py in
# development, or in /etc/tower/conf.d/<something>.py)
defaults = request.node.get_marker('defined_in_file')
if defaults:
settings.configure(**defaults.kwargs)
settings._wrapped = SettingsWrapper(settings._wrapped,
@ -280,7 +280,7 @@ def test_field_with_custom_mixin(reg):
assert field.is_great() is True
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
@pytest.mark.defined_in_file(AWX_SOME_SETTING='DEFAULT')
def test_default_value_from_settings(reg):
reg.register(
'AWX_SOME_SETTING',
@ -293,7 +293,7 @@ def test_default_value_from_settings(reg):
assert field.default == 'DEFAULT'
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
@pytest.mark.defined_in_file(AWX_SOME_SETTING='DEFAULT')
def test_default_value_from_settings_with_custom_representation(reg):
class LowercaseCharField(fields.CharField):

View File

@ -40,12 +40,12 @@ def settings(request):
settings = LazySettings()
registry = SettingsRegistry(settings)
# @pytest.mark.readonly can be used to mark specific setting values as
# "read-only". This is analogous to manually specifying a setting on the
# filesystem (e.g., in a local_settings.py in development, or in
# /etc/tower/conf.d/<something>.py)
readonly_marker = request.node.get_marker('readonly')
defaults = readonly_marker.kwargs if readonly_marker else {}
# @pytest.mark.defined_in_file can be used to mark specific setting values
# as "defined in a settings file". This is analogous to manually
# specifying a setting on the filesystem (e.g., in a local_settings.py in
# development, or in /etc/tower/conf.d/<something>.py)
in_file_marker = request.node.get_marker('defined_in_file')
defaults = in_file_marker.kwargs if in_file_marker else {}
defaults['DEFAULTS_SNAPSHOT'] = {}
settings.configure(**defaults)
settings._wrapped = SettingsWrapper(settings._wrapped,
@ -54,14 +54,14 @@ def settings(request):
return settings
@pytest.mark.readonly(DEBUG=True)
@pytest.mark.defined_in_file(DEBUG=True)
def test_unregistered_setting(settings):
"native Django settings are not stored in DB, and aren't cached"
assert settings.DEBUG is True
assert settings.cache.get('DEBUG') is None
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
@pytest.mark.defined_in_file(AWX_SOME_SETTING='DEFAULT')
def test_read_only_setting(settings):
settings.registry.register(
'AWX_SOME_SETTING',
@ -75,7 +75,7 @@ def test_read_only_setting(settings):
assert settings == ['AWX_SOME_SETTING']
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
@pytest.mark.defined_in_file(AWX_SOME_SETTING='DEFAULT')
def test_read_only_setting_with_empty_default(settings):
settings.registry.register(
'AWX_SOME_SETTING',
@ -90,7 +90,7 @@ def test_read_only_setting_with_empty_default(settings):
assert settings == ['AWX_SOME_SETTING']
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
@pytest.mark.defined_in_file(AWX_SOME_SETTING='DEFAULT')
def test_read_only_defaults_are_cached(settings):
"read-only settings are stored in the cache"
settings.registry.register(
@ -103,7 +103,7 @@ def test_read_only_defaults_are_cached(settings):
assert settings.cache.get('AWX_SOME_SETTING') == 'DEFAULT'
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
@pytest.mark.defined_in_file(AWX_SOME_SETTING='DEFAULT')
def test_cache_respects_timeout(settings):
"only preload the cache every SETTING_CACHE_TIMEOUT settings"
settings.registry.register(
@ -137,6 +137,33 @@ def test_default_setting(settings, mocker):
assert settings.cache.get('AWX_SOME_SETTING') == 'DEFAULT'
@pytest.mark.defined_in_file(AWX_SOME_SETTING='DEFAULT')
def test_setting_is_from_setting_file(settings, mocker):
settings.registry.register(
'AWX_SOME_SETTING',
field_class=fields.CharField,
category=_('System'),
category_slug='system'
)
assert settings.AWX_SOME_SETTING == 'DEFAULT'
assert settings.registry.get_setting_field('AWX_SOME_SETTING').defined_in_file is True
def test_setting_is_not_from_setting_file(settings, mocker):
settings.registry.register(
'AWX_SOME_SETTING',
field_class=fields.CharField,
category=_('System'),
category_slug='system',
default='DEFAULT'
)
settings_to_cache = mocker.Mock(**{'order_by.return_value': []})
with mocker.patch('awx.conf.models.Setting.objects.filter', return_value=settings_to_cache):
assert settings.AWX_SOME_SETTING == 'DEFAULT'
assert settings.registry.get_setting_field('AWX_SOME_SETTING').defined_in_file is False
def test_empty_setting(settings, mocker):
"settings with no default and no defined value are not valid"
settings.registry.register(
@ -180,7 +207,7 @@ def test_setting_from_db(settings, mocker):
assert settings.cache.get('AWX_SOME_SETTING') == 'FROM_DB'
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
@pytest.mark.defined_in_file(AWX_SOME_SETTING='DEFAULT')
def test_read_only_setting_assignment(settings):
"read-only settings cannot be overwritten"
settings.registry.register(
@ -255,7 +282,7 @@ def test_db_setting_deletion(settings, mocker):
assert existing_setting.delete.call_count == 1
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
@pytest.mark.defined_in_file(AWX_SOME_SETTING='DEFAULT')
def test_read_only_setting_deletion(settings):
"read-only settings cannot be deleted"
settings.registry.register(