feat: env_prefix create_app variable can select the environment var prefix

This commit is contained in:
Éloi Rivard 2024-04-22 18:10:49 +02:00
parent b8645ce1a3
commit afa0a6ff1e
No known key found for this signature in database
GPG key ID: 7EDA204EA57DD184
4 changed files with 43 additions and 15 deletions

View file

@ -1,9 +1,13 @@
Added
^^^^^
- `env_prefix` create_app variable can select the environment var prefix.
[0.0.52] - 2024-04-22 [0.0.52] - 2024-04-22
--------------------- ---------------------
Added Added
^^^^^ ^^^^^
- `ENV_FILE` environment variable can customize/disable the .env file - `env_file` create_app variable can customize/disable the .env file
Changed Changed
^^^^^^^ ^^^^^^^

View file

@ -124,7 +124,9 @@ def setup_flask_converters(app):
app.url_map.converters[model_name.lower()] = model_converter(model_class) app.url_map.converters[model_name.lower()] = model_converter(model_class)
def create_app(config=None, validate=True, backend=None): def create_app(
config=None, validate=True, backend=None, env_file=".env", env_prefix=""
):
from .app.configuration import setup_config from .app.configuration import setup_config
from .app.i18n import setup_i18n from .app.i18n import setup_i18n
from .app.themes import setup_themer from .app.themes import setup_themer
@ -132,7 +134,13 @@ def create_app(config=None, validate=True, backend=None):
app = Flask(__name__) app = Flask(__name__)
with app.app_context(): with app.app_context():
if not setup_config(app, config, validate): # pragma: no cover if not setup_config(
app=app,
config=config,
test_config=validate,
env_file=env_file,
env_prefix=env_prefix,
): # pragma: no cover
sys.exit(1) sys.exit(1)
sentry_sdk = setup_sentry(app) sentry_sdk = setup_sentry(app)

View file

@ -13,7 +13,6 @@ from pydantic_settings import SettingsConfigDict
from canaille.core.configuration import CoreSettings from canaille.core.configuration import CoreSettings
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DEFAULT_ENV_FILE = ".env"
class RootSettings(BaseSettings): class RootSettings(BaseSettings):
@ -60,10 +59,10 @@ class RootSettings(BaseSettings):
""" """
def settings_factory(config): def settings_factory(config, env_file=".env", env_prefix=""):
"""Overly complicated function that pushes the backend specific """Pushes the backend specific configuration into CoreSettings, in the
configuration into CoreSettings, in the purpose break dependency against purpose break dependency against backends libraries like python-ldap or
backends libraries like python-ldap or sqlalchemy.""" sqlalchemy."""
attributes = {"CANAILLE": (CoreSettings, CoreSettings())} attributes = {"CANAILLE": (CoreSettings, CoreSettings())}
if "CANAILLE_SQL" in config or any( if "CANAILLE_SQL" in config or any(
@ -93,11 +92,11 @@ def settings_factory(config):
**attributes, **attributes,
) )
env_file = os.getenv("ENV_FILE", config.get("ENV_FILE", DEFAULT_ENV_FILE))
return Settings( return Settings(
**config, **config,
_secrets_dir=os.environ.get("SECRETS_DIR"), _secrets_dir=os.environ.get("SECRETS_DIR"),
_env_file=env_file, _env_file=env_file,
_env_prefix=env_prefix,
) )
@ -121,7 +120,7 @@ def toml_content(file_path):
raise Exception("toml library not installed. Cannot load configuration.") raise Exception("toml library not installed. Cannot load configuration.")
def setup_config(app, config=None, test_config=True): def setup_config(app, config=None, test_config=True, env_file=".env", env_prefix=""):
from canaille.oidc.installation import install from canaille.oidc.installation import install
app.config.from_mapping( app.config.from_mapping(
@ -135,7 +134,9 @@ def setup_config(app, config=None, test_config=True):
config = toml_content(os.environ.get("CONFIG")) config = toml_content(os.environ.get("CONFIG"))
try: try:
config_obj = settings_factory(config or {}) config_obj = settings_factory(
config or {}, env_file=env_file, env_prefix=env_prefix
)
except ValidationError as exc: # pragma: no cover except ValidationError as exc: # pragma: no cover
app.logger.critical(str(exc)) app.logger.critical(str(exc))
return False return False

View file

@ -41,6 +41,7 @@ def test_configuration_nestedsecrets_directory(tmp_path, backend, configuration)
def test_configuration_from_environment_vars(): def test_configuration_from_environment_vars():
"""Canaille should read configuration from environment vars."""
os.environ["SECRET_KEY"] = "very-very-secret" os.environ["SECRET_KEY"] = "very-very-secret"
os.environ["CANAILLE__SMTP__FROM_ADDR"] = "user@mydomain.tld" os.environ["CANAILLE__SMTP__FROM_ADDR"] = "user@mydomain.tld"
os.environ["CANAILLE_SQL__DATABASE_URI"] = "sqlite:///anything.db" os.environ["CANAILLE_SQL__DATABASE_URI"] = "sqlite:///anything.db"
@ -60,6 +61,22 @@ def test_configuration_from_environment_vars():
del os.environ["CANAILLE_SQL__DATABASE_URI"] del os.environ["CANAILLE_SQL__DATABASE_URI"]
def test_disable_env_var_loading(tmp_path, configuration):
"""Canaille should not read configuration from environment vars when
env_prefix is False."""
del configuration["SERVER_NAME"]
os.environ["SERVER_NAME"] = "example.com"
os.environ["FOOBAR_SERVER_NAME"] = "foobar.example.com"
app = create_app(configuration, env_prefix="")
assert app.config["SERVER_NAME"] == "example.com"
app = create_app(configuration, env_prefix="FOOBAR_")
assert app.config["SERVER_NAME"] == "foobar.example.com"
del os.environ["SERVER_NAME"]
def test_dotenv_file(tmp_path, configuration): def test_dotenv_file(tmp_path, configuration):
"""Canaille should read configuration from .env files.""" """Canaille should read configuration from .env files."""
oldcwd = os.getcwd() oldcwd = os.getcwd()
@ -81,8 +98,7 @@ def test_custom_dotenv_file(tmp_path, configuration):
with open(dotenv, "w") as fd: with open(dotenv, "w") as fd:
fd.write("FOOBAR=other-custom-value") fd.write("FOOBAR=other-custom-value")
configuration["ENV_FILE"] = dotenv app = create_app(configuration, env_file=dotenv)
app = create_app(configuration)
assert app.config["FOOBAR"] == "other-custom-value" assert app.config["FOOBAR"] == "other-custom-value"
@ -95,8 +111,7 @@ def test_disable_dotenv_file(tmp_path, configuration):
with open(dotenv, "w") as fd: with open(dotenv, "w") as fd:
fd.write("FOOBAR=custom-value") fd.write("FOOBAR=custom-value")
configuration["ENV_FILE"] = None app = create_app(configuration, env_file=None)
app = create_app(configuration)
assert "FOOBAR" not in app.config assert "FOOBAR" not in app.config
os.chdir(oldcwd) os.chdir(oldcwd)