From 086722149c264c38a2b8e7e4f2a17f39a3837716 Mon Sep 17 00:00:00 2001 From: Alan Rominger Date: Thu, 20 Apr 2023 15:15:34 -0400 Subject: [PATCH] Avoid recursive include of DEFAULT_SETTINGS, add sanity test (#13236) * Avoid recursive include of DEFAULT_SETTINGS, add sanity test to avoid similar surprises * Implement review comments for more clear code order and readability * Clarify comment about order of app name, which is last in order so that it can modify user settings --- awx/main/tests/unit/test_settings.py | 23 +++++++++++++++ awx/settings/development.py | 44 ++++++++++++++++++---------- awx/settings/production.py | 18 +++++++----- 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/awx/main/tests/unit/test_settings.py b/awx/main/tests/unit/test_settings.py index 19b90099a1..ef516dbefc 100644 --- a/awx/main/tests/unit/test_settings.py +++ b/awx/main/tests/unit/test_settings.py @@ -1,8 +1,31 @@ from split_settings.tools import include +LOCAL_SETTINGS = ( + 'ALLOWED_HOSTS', + 'BROADCAST_WEBSOCKET_PORT', + 'BROADCAST_WEBSOCKET_VERIFY_CERT', + 'BROADCAST_WEBSOCKET_PROTOCOL', + 'BROADCAST_WEBSOCKET_SECRET', + 'DATABASES', + 'DEBUG', + 'NAMED_URL_GRAPH', +) + + def test_postprocess_auth_basic_enabled(): locals().update({'__file__': __file__}) include('../../../settings/defaults.py', scope=locals()) assert 'awx.api.authentication.LoggedBasicAuthentication' in locals()['REST_FRAMEWORK']['DEFAULT_AUTHENTICATION_CLASSES'] + + +def test_default_settings(): + from django.conf import settings + + for k in dir(settings): + if k not in settings.DEFAULTS_SNAPSHOT or k in LOCAL_SETTINGS: + continue + default_val = getattr(settings.default_settings, k, None) + snapshot_val = settings.DEFAULTS_SNAPSHOT[k] + assert default_val == snapshot_val, f'Setting for {k} does not match shapshot:\nsnapshot: {snapshot_val}\ndefault: {default_val}' diff --git a/awx/settings/development.py b/awx/settings/development.py index b8b911b07c..ad739158b3 100644 --- a/awx/settings/development.py +++ b/awx/settings/development.py @@ -62,19 +62,6 @@ DEBUG_TOOLBAR_CONFIG = {'ENABLE_STACKTRACES': True} SYSTEM_UUID = '00000000-0000-0000-0000-000000000000' INSTALL_UUID = '00000000-0000-0000-0000-000000000000' -# Store a snapshot of default settings at this point before loading any -# customizable config files. -DEFAULTS_SNAPSHOT = {} -this_module = sys.modules[__name__] -for setting in dir(this_module): - if setting == setting.upper(): - DEFAULTS_SNAPSHOT[setting] = copy.deepcopy(getattr(this_module, setting)) - -# If there is an `/etc/tower/settings.py`, include it. -# If there is a `/etc/tower/conf.d/*.py`, include them. -include(optional('/etc/tower/settings.py'), scope=locals()) -include(optional('/etc/tower/conf.d/*.py'), scope=locals()) - BASE_VENV_PATH = "/var/lib/awx/venv/" AWX_VENV_PATH = os.path.join(BASE_VENV_PATH, "awx") @@ -105,11 +92,28 @@ AWX_CALLBACK_PROFILE = True AWX_DISABLE_TASK_MANAGERS = False # ======================!!!!!!! FOR DEVELOPMENT ONLY !!!!!!!================================= -from .application_name import set_application_name +# Store a snapshot of default settings at this point before loading any +# customizable config files. +this_module = sys.modules[__name__] +local_vars = dir(this_module) +DEFAULTS_SNAPSHOT = {} # define after we save local_vars so we do not snapshot the snapshot +for setting in local_vars: + if setting.isupper(): + DEFAULTS_SNAPSHOT[setting] = copy.deepcopy(getattr(this_module, setting)) -set_application_name(DATABASES, CLUSTER_HOST_ID) +del local_vars # avoid temporary variables from showing up in dir(settings) +del this_module +# +############################################################################################### +# +# Any settings defined after this point will be marked as as a read_only database setting +# +################################################################################################ -del set_application_name +# If there is an `/etc/tower/settings.py`, include it. +# If there is a `/etc/tower/conf.d/*.py`, include them. +include(optional('/etc/tower/settings.py'), scope=locals()) +include(optional('/etc/tower/conf.d/*.py'), scope=locals()) # If any local_*.py files are present in awx/settings/, use them to override # default settings for development. If not present, we can still run using @@ -123,3 +127,11 @@ try: except ImportError: traceback.print_exc() sys.exit(1) + +# The below runs AFTER all of the custom settings are imported +# because conf.d files will define DATABASES and this should modify that +from .application_name import set_application_name + +set_application_name(DATABASES, CLUSTER_HOST_ID) + +del set_application_name diff --git a/awx/settings/production.py b/awx/settings/production.py index 4f25d274b1..97d7abbe10 100644 --- a/awx/settings/production.py +++ b/awx/settings/production.py @@ -47,17 +47,21 @@ AWX_ISOLATION_SHOW_PATHS = [ # Store a snapshot of default settings at this point before loading any # customizable config files. +this_module = sys.modules[__name__] +local_vars = dir(this_module) +DEFAULTS_SNAPSHOT = {} # define after we save local_vars so we do not snapshot the snapshot +for setting in local_vars: + if setting.isupper(): + DEFAULTS_SNAPSHOT[setting] = copy.deepcopy(getattr(this_module, setting)) + +del local_vars # avoid temporary variables from showing up in dir(settings) +del this_module # ############################################################################################### # # Any settings defined after this point will be marked as as a read_only database setting # ################################################################################################ -DEFAULTS_SNAPSHOT = {} -this_module = sys.modules[__name__] -for setting in dir(this_module): - if setting == setting.upper(): - DEFAULTS_SNAPSHOT[setting] = copy.deepcopy(getattr(this_module, setting)) # Load settings from any .py files in the global conf.d directory specified in # the environment, defaulting to /etc/tower/conf.d/. @@ -98,8 +102,8 @@ except IOError: else: raise -# The below runs AFTER all of the custom settings are imported. - +# The below runs AFTER all of the custom settings are imported +# because conf.d files will define DATABASES and this should modify that from .application_name import set_application_name set_application_name(DATABASES, CLUSTER_HOST_ID) # NOQA