From f8c31e4dcfa2385ce858eeadf4d33694629824d8 Mon Sep 17 00:00:00 2001 From: dandds Date: Thu, 5 Dec 2019 14:11:35 -0500 Subject: [PATCH] Add function for Flask app to read config from a directory. The application now checks for an environment variable, OVERRIDE_CONFIG_DIRECTORY. If that value is set, it loops all the files in the specified directory and checks if their names correspond to known configuration settings. The contents of any matching files are read and set as the new configuration value for that setting. This will allow us to read mounted Azure Key Vault items as config values. This also moves the functionality for applying environment variables to the config into an analogous function. --- atst/app.py | 42 +++++++++++++++++++++++++++++++++++------- tests/test_app.py | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/atst/app.py b/atst/app.py index 1b60f64c..e9daabd6 100644 --- a/atst/app.py +++ b/atst/app.py @@ -200,23 +200,21 @@ def make_config(direct_config=None): ENV_CONFIG_FILENAME = os.path.join( os.path.dirname(__file__), "../config/", "{}.ini".format(ENV.lower()) ) - OVERRIDE_CONFIG_FILENAME = os.getenv("OVERRIDE_CONFIG_FULLPATH") + OVERRIDE_CONFIG_DIRECTORY = os.getenv("OVERRIDE_CONFIG_DIRECTORY") config = ConfigParser(allow_no_value=True) config.optionxform = str config_files = [BASE_CONFIG_FILENAME, ENV_CONFIG_FILENAME] - if OVERRIDE_CONFIG_FILENAME: - config_files.append(OVERRIDE_CONFIG_FILENAME) # ENV_CONFIG will override values in BASE_CONFIG. config.read(config_files) + if OVERRIDE_CONFIG_DIRECTORY: + apply_config_from_directory(OVERRIDE_CONFIG_DIRECTORY, config) + # Check for ENV variables as a final source of overrides - for confsetting in config.options("default"): - env_override = os.getenv(confsetting.upper()) - if env_override: - config.set("default", confsetting, env_override) + apply_config_from_environment(config) # override if a dictionary of options has been given if direct_config: @@ -244,6 +242,36 @@ def make_config(direct_config=None): return map_config(config) +def apply_config_from_directory(config_dir, config, section="default"): + """ + Loop files in a directory, check if the names correspond to + known config values, and apply the file contents as the value + for that setting if they do. + """ + for confsetting in os.listdir(config_dir): + if confsetting in config.options(section): + full_path = os.path.join(config_dir, confsetting) + with open(full_path, "r") as conf_file: + config.set(section, confsetting, conf_file.read().strip()) + + return config + + +def apply_config_from_environment(config, section="default"): + """ + Loops all the configuration settins in a given section of a + config object and checks whether those settings also exist as + environment variables. If so, it applies the environment + variables value as the new configuration setting value. + """ + for confsetting in config.options(section): + env_override = os.getenv(confsetting.upper()) + if env_override: + config.set(section, confsetting, env_override) + + return config + + def make_redis(app, config): r = redis.Redis.from_url(config["REDIS_URI"]) app.redis = r diff --git a/tests/test_app.py b/tests/test_app.py index 222f4a4f..937a15e2 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -1,8 +1,13 @@ import os +from configparser import ConfigParser import pytest -from atst.app import make_crl_validator +from atst.app import ( + make_crl_validator, + apply_config_from_directory, + apply_config_from_environment, +) @pytest.fixture @@ -22,3 +27,43 @@ def test_make_crl_validator_creates_crl_dir(app, tmpdir, replace_crl_dir_config) replace_crl_dir_config(crl_dir) make_crl_validator(app) assert os.path.isdir(crl_dir) + + +@pytest.fixture +def config_object(): + config = ConfigParser() + config.optionxform = str + config.read_string("[default]\nFOO=BALONEY") + return config + + +def test_apply_config_from_directory(tmpdir, config_object): + config_setting = tmpdir.join("FOO") + with open(config_setting, "w") as conf_file: + conf_file.write("MAYO") + + apply_config_from_directory(tmpdir, config_object) + assert config_object.get("default", "FOO") == "MAYO" + + +def test_apply_config_from_directory_skips_unknown_settings(tmpdir, config_object): + config_setting = tmpdir.join("FLARF") + with open(config_setting, "w") as conf_file: + conf_file.write("MAYO") + + apply_config_from_directory(tmpdir, config_object) + assert "FLARF" not in config_object.options("default") + + +def test_apply_config_from_environment(monkeypatch, config_object): + monkeypatch.setenv("FOO", "MAYO") + apply_config_from_environment(config_object) + assert config_object.get("default", "FOO") == "MAYO" + + +def test_apply_config_from_environment_skips_unknown_settings( + monkeypatch, config_object +): + monkeypatch.setenv("FLARF", "MAYO") + apply_config_from_environment(config_object) + assert "FLARF" not in config_object.options("default")