Source code for fmn.core.config
# SPDX-FileCopyrightText: Contributors to the Fedora Project
#
# SPDX-License-Identifier: MIT
from datetime import timedelta
from functools import cache
from pathlib import Path
from typing import Any
from pydantic import BaseModel, ConfigDict, DirectoryPath, model_validator
from pydantic_settings import BaseSettings
DEFAULT_CONFIG_FILE = _settings_file = "/etc/fmn/fmn.cfg"
TOP_DIR = Path(__file__).parent.parent
CashewsTTLTypes = int | float | str | timedelta
[docs]
class CacheArgsModel(BaseModel):
ttl: CashewsTTLTypes | None = None
lock_ttl: CashewsTTLTypes | None = None
early_ttl: CashewsTTLTypes | None = None
[docs]
class CacheScopedArgsModel(BaseModel):
tracked: CacheArgsModel = CacheArgsModel(ttl="1d", lock_ttl="1h", early_ttl="20h")
rules: CacheArgsModel = CacheArgsModel(ttl="1d", lock_ttl="5m", early_ttl="20h")
pagure: CacheArgsModel | None = None
fasjson: CacheArgsModel | None = None
[docs]
class CacheModel(BaseModel):
url: str = "mem://"
setup_args: dict[str, Any] | None = None
backoff_max_tries: int = 5
default_args: CacheArgsModel = CacheArgsModel(ttl="1h")
scoped_args: CacheScopedArgsModel = CacheScopedArgsModel()
[docs]
class SQLAlchemyModel(BaseModel):
model_config = ConfigDict(extra="allow")
url: str = "sqlite:///:memory:"
echo: bool = False
isolation_level: str = "SERIALIZABLE"
[docs]
class AlembicModel(BaseModel):
migrations_path: DirectoryPath = TOP_DIR.joinpath("database").joinpath("migrations").absolute()
[docs]
class DBModel(BaseModel):
sqlalchemy: SQLAlchemyModel = SQLAlchemyModel()
alembic: AlembicModel = AlembicModel()
[docs]
class ServicesModel(BaseModel):
fasjson_url: str = "https://fasjson.fedoraproject.org"
distgit_url: str = "https://src.fedoraproject.org"
distgit_db_url: str | None = None
datagrepper_url: str = "https://apps.fedoraproject.org/datagrepper"
[docs]
class Settings(BaseSettings):
public_url: str = "https://notifications.fedoraproject.org"
cors_origins: str = "https://notifications.fedoraproject.org"
oidc_provider_url: str = "https://id.fedoraproject.org/openidc"
oidc_conf_endpoint: str = "/.well-known/openid-configuration"
oidc_token_info_endpoint: str = "/TokenInfo" # noqa: S105
oidc_user_info_endpoint: str = "/UserInfo"
oidc_client_id: str = "0123456789abcdef0123456789abcdef"
oidc_client_secret: str = "0123456789abcdef0123456789abcdef" # noqa: S105
admin_groups: list[str] = ["sysadmin-main"]
# these fields are computed from the above
oidc_conf_url: str | None = None
oidc_token_info_url: str | None = None
oidc_user_info_url: str | None = None
id_cache_gc_interval: int = 300
database: DBModel = DBModel()
cache: CacheModel = CacheModel()
services: ServicesModel = ServicesModel()
model_config = ConfigDict(env_file="fmn.cfg", env_nested_delimiter="__")
[docs]
@model_validator(mode="after")
def compute_compound_fields(self) -> dict:
self.oidc_conf_url = (
self.oidc_provider_url.rstrip("/") + "/" + self.oidc_conf_endpoint.lstrip("/")
)
self.oidc_token_info_url = (
self.oidc_provider_url.rstrip("/") + "/" + self.oidc_token_info_endpoint.lstrip("/")
)
self.oidc_user_info_url = (
self.oidc_provider_url.rstrip("/") + "/" + self.oidc_user_info_endpoint.lstrip("/")
)
return self
[docs]
@cache
def get_settings() -> Settings:
return Settings(_env_file=_settings_file)
[docs]
def set_settings_file(path: str) -> None:
global _settings_file
_settings_file = path
get_settings.cache_clear()