|
3 | 3 | # (c) Technische Universität Berlin, innoCampus <info@isis.tu-berlin.de> |
4 | 4 | import builtins |
5 | 5 | import logging |
| 6 | +import os |
| 7 | +import re |
6 | 8 | from datetime import timedelta |
7 | 9 | from pathlib import Path |
8 | 10 | from pydoc import locate |
9 | | -from typing import Any, ClassVar, Final, Literal |
| 11 | +from typing import Any, ClassVar, Final, Literal, Self |
10 | 12 |
|
11 | 13 | import semver |
12 | 14 | import yaml |
13 | | -from pydantic import BaseModel, ByteSize, DirectoryPath, HttpUrl, PositiveInt, conset, field_validator |
14 | | -from pydantic.fields import FieldInfo |
| 15 | +from pydantic import ( |
| 16 | + BaseModel, |
| 17 | + ByteSize, |
| 18 | + DirectoryPath, |
| 19 | + HttpUrl, |
| 20 | + PositiveInt, |
| 21 | + RootModel, |
| 22 | + conset, |
| 23 | + field_validator, |
| 24 | + model_validator, |
| 25 | +) |
| 26 | +from pydantic.fields import Field, FieldInfo |
15 | 27 | from pydantic_settings import ( |
16 | 28 | BaseSettings, |
17 | 29 | EnvSettingsSource, |
|
20 | 32 | SettingsConfigDict, |
21 | 33 | ) |
22 | 34 |
|
23 | | -from questionpy_common.constants import MAX_PACKAGE_SIZE, GiB, MiB |
| 35 | +from questionpy_common.constants import ENVIRONMENT_VARIABLE, ENVIRONMENT_VARIABLE_REGEX, MAX_PACKAGE_SIZE, GiB, MiB |
24 | 36 | from questionpy_common.manifest import PartialPackagePermissions, ensure_is_valid_name |
25 | 37 | from questionpy_server.worker import Worker |
26 | 38 | from questionpy_server.worker.impl.subprocess import SubprocessWorker |
@@ -155,8 +167,11 @@ def validate_name(cls, value: str) -> str: |
155 | 167 | MainProcessExecutionModeValues = {"container", "trusted"} |
156 | 168 |
|
157 | 169 |
|
158 | | -class SpecificPackagePermissions(BaseModel): |
| 170 | +class Selectable(BaseModel): |
159 | 171 | package_selector: PackageSelector = PackageSelector() |
| 172 | + |
| 173 | + |
| 174 | +class SpecificPackagePermissions(Selectable): |
160 | 175 | auto_grant_permissions: PartialPackagePermissions | None = None |
161 | 176 | override_permissions: PartialPackagePermissions | None = None |
162 | 177 |
|
@@ -195,6 +210,35 @@ class PackagePermissionsSettings(BaseModel): |
195 | 210 | packages: list[SpecificPackagePermissions] = [] |
196 | 211 |
|
197 | 212 |
|
| 213 | +class EnvironmentVariables(RootModel[dict[ENVIRONMENT_VARIABLE, str]]): |
| 214 | + interpolation_pattern: ClassVar[re.Pattern] = re.compile(rf"^\$\{{({ENVIRONMENT_VARIABLE_REGEX})}}$") |
| 215 | + escaped_interpolation_pattern: ClassVar[re.Pattern] = re.compile(rf"^\$(\$+\{{{ENVIRONMENT_VARIABLE_REGEX}}})$") |
| 216 | + |
| 217 | + @model_validator(mode="after") |
| 218 | + def check_environment_variables(self) -> Self: |
| 219 | + for key, value in self.root.items(): |
| 220 | + if match := self.interpolation_pattern.match(value): |
| 221 | + interpolated_key = match.group(1) |
| 222 | + if interpolated_key not in os.environ: |
| 223 | + msg = f"Environment variable '{interpolated_key}' not found." |
| 224 | + raise ValueError(msg) |
| 225 | + self.root[key] = os.environ[interpolated_key] |
| 226 | + _log.debug("Interpolated environment variable: %s=%s.", key, self.root[key]) |
| 227 | + elif match := self.escaped_interpolation_pattern.match(value): |
| 228 | + self.root[key] = match.group(1) |
| 229 | + _log.debug("Escaped environment variable: %s=%s.", key, self.root[key]) |
| 230 | + return self |
| 231 | + |
| 232 | + |
| 233 | +class SpecificPackageEnvironmentVariables(Selectable): |
| 234 | + environment_variables: EnvironmentVariables | None = None |
| 235 | + |
| 236 | + |
| 237 | +class EnvironmentVariablesSettings(BaseModel): |
| 238 | + global_: EnvironmentVariables = Field(alias="global", default=EnvironmentVariables({})) |
| 239 | + packages: list[SpecificPackageEnvironmentVariables] = [] |
| 240 | + |
| 241 | + |
198 | 242 | class CacheSettings(BaseModel): |
199 | 243 | size: ByteSize = ByteSize(1 * GiB) |
200 | 244 | directory: DirectoryPath = Path("cache").resolve() |
@@ -274,6 +318,7 @@ class Settings(BaseSettings): |
274 | 318 | webservice: WebserviceSettings |
275 | 319 | worker_pool: WorkerPoolSettings |
276 | 320 | permissions: PackagePermissionsSettings |
| 321 | + environment_variables: EnvironmentVariablesSettings |
277 | 322 | cache: CacheSettings |
278 | 323 | collector: CollectorSettings |
279 | 324 | auth: AuthSettings |
|
0 commit comments