diff --git a/diracx-core/src/diracx/core/settings.py b/diracx-core/src/diracx/core/settings.py index dd55a88a8..40cf29294 100644 --- a/diracx-core/src/diracx/core/settings.py +++ b/diracx-core/src/diracx/core/settings.py @@ -32,10 +32,12 @@ SecretStr, TypeAdapter, UrlConstraints, + create_model, model_validator, ) from pydantic_settings import BaseSettings, SettingsConfigDict +from .extensions import DiracEntryPoint, select_from_extension from .properties import SecurityProperty from .s3 import s3_bucket_exists @@ -358,3 +360,64 @@ def s3_client(self) -> S3Client: if self._client is None: raise RuntimeError("S3 client accessed before lifetime function") return self._client + + +def _build_factory_settings_model() -> type[ServiceSettingsBase]: + + class _EnabledServicesBase(ServiceSettingsBase): + model_config = SettingsConfigDict( + frozen=True, + use_attribute_docstrings=True, + ) + + enabled_services_field: dict[str, tuple[Any, Any]] = {} + + for entry_point in select_from_extension(group=DiracEntryPoint.SERVICES): + if "well-known" in entry_point.name: + continue + enabled_services_field[f"{entry_point.name}"] = ( + bool, + Field( + default=True, + validation_alias=f"DIRACX_SERVICE_{entry_point.name.upper()}_ENABLED", + description=f"Enable the {entry_point.name.upper()} router", + ), + ) + + EnabledServices = create_model( # noqa: N806 + "EnabledServices", + __doc__="Enabled services", + __base__=_EnabledServicesBase, + **cast(dict[str, Any], enabled_services_field), + ) + + class _BaseFactorySettings(ServiceSettingsBase): + """Factory settings. + + Settings which do not fit into dedicated classes, + or are dynamically generated. + """ + + model_config = SettingsConfigDict(use_attribute_docstrings=True) + + diracx_legacy_exchange_hashed_api_key: str = "" + """The hashed API key for the legacy exchange endpoint. + """ + enabled_services: EnabledServices = Field( + default_factory=EnabledServices, + # TODO fix the variable doc generation + description=str(cast(BaseSettings, EnabledServices).schema_json()), + ) + + fields: dict[str, tuple[Any, Any]] = {} + + new_mod = create_model( + "FactorySettings", + __doc__=_BaseFactorySettings.__doc__, + __base__=_BaseFactorySettings, + **cast(dict[str, Any], fields), + ) + return new_mod + + +FactorySettings = _build_factory_settings_model() diff --git a/diracx-routers/src/diracx/routers/factory.py b/diracx-routers/src/diracx/routers/factory.py index 342e6d5cd..22911bf9c 100644 --- a/diracx-routers/src/diracx/routers/factory.py +++ b/diracx-routers/src/diracx/routers/factory.py @@ -6,7 +6,6 @@ import inspect import logging -import os from collections.abc import AsyncGenerator, Awaitable, Callable, Iterable, Sequence from functools import partial from http import HTTPStatus @@ -24,14 +23,13 @@ from fastapi.responses import JSONResponse, Response from fastapi.routing import APIRoute from packaging.version import InvalidVersion, parse -from pydantic import TypeAdapter from starlette.middleware.base import BaseHTTPMiddleware from uvicorn.logging import AccessFormatter, DefaultFormatter from diracx.core.config import ConfigSource from diracx.core.exceptions import DiracError, DiracHttpResponseError, NotReadyError from diracx.core.extensions import DiracEntryPoint, select_from_extension -from diracx.core.settings import ServiceSettingsBase +from diracx.core.settings import FactorySettings, ServiceSettingsBase from diracx.core.utils import dotenv_files_from_environment from diracx.db.exceptions import DBUnavailableError from diracx.db.os.utils import BaseOSDB @@ -354,9 +352,13 @@ def create_app() -> DiracFastAPI: # Load all available routers enabled_systems = set() settings_classes = set() + factory_settings = FactorySettings() for entry_point in select_from_extension(group=DiracEntryPoint.SERVICES): - env_var = f"DIRACX_SERVICE_{entry_point.name.upper()}_ENABLED" - enabled = TypeAdapter(bool).validate_json(os.environ.get(env_var, "true")) + enabled = getattr( + factory_settings.enabled_services, + entry_point.name, + True, + ) logger.debug("Found service %r: enabled=%s", entry_point, enabled) if not enabled: continue diff --git a/docs/admin/reference/env-variables.md b/docs/admin/reference/env-variables.md index ea9a04a49..bc486f777 100644 --- a/docs/admin/reference/env-variables.md +++ b/docs/admin/reference/env-variables.md @@ -13,10 +13,31 @@ The URL of the configuration backend. The variable points to .env files where configuration may be placed. There could be more than one file, with suffixes \_X, where X is a number. The files will be loaded in order. -### `DIRACX_SERVICE_JOBS_ENABLED` +### `DIRACX_SERVICE_JOBSjinga_ENABLED` Determines whether the jobs service is enabled. +## FactorySettings + +Factory settings. + +``` + Settings which do not fit into dedicated classes, + or are dynamically generated. +``` + +### `DIRACX_LEGACY_EXCHANGE_HASHED_API_KEY` + +*Optional*, default value: \`\` + +The hashed API key for the legacy exchange endpoint. + +### `ENABLED_SERVICES` + +*Optional* + +{"additionalProperties": false, "description": "Enabled services", "properties": {"DIRACX_SERVICE_AUTH_ENABLED": {"default": true, "description": "Enable the AUTH router", "title": "Diracx Service Auth Enabled", "type": "boolean"}, "DIRACX_SERVICE_CONFIG_ENABLED": {"default": true, "description": "Enable the CONFIG router", "title": "Diracx Service Config Enabled", "type": "boolean"}, "DIRACX_SERVICE_HEALTH_ENABLED": {"default": true, "description": "Enable the HEALTH router", "title": "Diracx Service Health Enabled", "type": "boolean"}, "DIRACX_SERVICE_JOBS_ENABLED": {"default": true, "description": "Enable the JOBS router", "title": "Diracx Service Jobs Enabled", "type": "boolean"}}, "title": "EnabledServices", "type": "object"} + ## AuthSettings Settings for the authentication service. @@ -138,10 +159,6 @@ Set of security properties available in this DIRAC installation. These properties define various authorization capabilities and are used for access control decisions. Defaults to all available security properties. -### `DIRACX_LEGACY_EXCHANGE_HASHED_API_KEY` - -The hashed API key for the legacy exchange endpoint. - ## SandboxStoreSettings Settings for the sandbox store. diff --git a/docs/admin/reference/env-variables.md.j2 b/docs/admin/reference/env-variables.md.j2 index 29cb4f078..518fd9db2 100644 --- a/docs/admin/reference/env-variables.md.j2 +++ b/docs/admin/reference/env-variables.md.j2 @@ -12,13 +12,13 @@ The URL of the configuration backend. The variable points to .env files where configuration may be placed. There could be more than one file, with suffixes _X, where X is a number. The files will be loaded in order. -### `DIRACX_SERVICE_JOBS_ENABLED` +### `DIRACX_SERVICE_JOBSjinga_ENABLED` Determines whether the jobs service is enabled. -{{ render_class('AuthSettings') }} +{{ render_class('FactorySettings') }} + -### `DIRACX_LEGACY_EXCHANGE_HASHED_API_KEY` -The hashed API key for the legacy exchange endpoint. +{{ render_class('AuthSettings') }} {{ render_class('SandboxStoreSettings') }}