"""This module contains the configuration for the application.
Depending on the environment, the configuration will be different.
"""
# https://rednafi.github.io/digressions/python/2020/06/03/python-configs.html
import os
from enum import Enum
from typing import List, Optional, Union
from pydantic import AnyHttpUrl, BaseSettings, Field, MongoDsn, validator
from ..models.engine import ServiceBackend
[docs]class Environment(str, Enum):
"""Enum for the different environments."""
DEV = "dev"
STG = "stg"
PROD = "prod"
TEST = "test"
[docs]class GlobalConfig(BaseSettings):
"""Global configuration for the application."""
ENV_STATE: Environment = Field(default=Environment.DEV, env="ENV_STATE")
# General Settings
# CORS ORIGINS
# e.g: '["http://localhost", "http://localhost:4200", "http://localhost:3000", \
# "http://localhost:8080", "http://local.dockertoolbox.tiangolo.com"]'
FRONTEND_HOST: List[AnyHttpUrl] = []
MAX_UPLOAD_SIZE_GB: Union[int, float] = Field(default=10)
SECURE_COOKIES: bool = Field(default=False) # set to True if site is HTTPS
# Authentication Settings
ALGORITHM: str = Field(default="HS256")
SECRET_KEY: Optional[
str
] = None # NOTE: set to none as a hack to get Sphinx to build correctly
# Database Settings
DB_NAME: str = Field(default="appStoreDB")
MONGO_DSN: Optional[MongoDsn] = None
MONGO_USERNAME: Optional[str] = None
MONGO_PASSWORD: Optional[str] = None
# Object Storage Settings
MINIO_DSN: Optional[str] = None
MINIO_API_HOST: Optional[str] = None
MINIO_BUCKET_NAME: str = Field(default="model-zoo")
MINIO_TLS: bool = Field(default=False)
MINIO_API_ACCESS_KEY: Optional[str] = None
MINIO_API_SECRET_KEY: Optional[str] = None
# Kubernetes and Inference Service Settings
IE_NAMESPACE: Optional[str] = None
IE_SERVICE_TYPE: ServiceBackend = Field(default=ServiceBackend.EMISSARY)
IE_DEFAULT_PROTOCOL: str = Field(default="http")
IE_DOMAIN: Optional[str] = None
IE_INGRESS_NAME: Optional[str] = None # TODO: Integrate this
IE_INGRESS_NAMESPACE: Optional[str] = None # TODO: Integrate this
K8S_HOST: Optional[str] = None
K8S_API_KEY: Optional[str] = None
# ClearML Settings
CLEARML_CONFIG_FILE: Optional[str] = None
CLEARML_WEB_HOST: Optional[str] = None
CLEARML_API_HOST: Optional[str] = None
CLEARML_FILES_HOST: Optional[str] = None
CLEARML_API_ACCESS_KEY: Optional[str] = None
CLEARML_API_SECRET_KEY: Optional[str] = None
KEYCLOAK_SERVER_URL: Optional[str] = None
KEYCLOAK_REALM_NAME: str = Field(default="CommonServices")
KEYCLOAK_CLIENT_ID: str = Field(default="ai-appstore-backend")
KEYCLOAK_CLIENT_SECRET_KEY: Optional[str] = None
KEYCLOAK_AUTHORIZATION_URL: Optional[str] = None
KEYCLOAK_TOKEN_URL: Optional[str] = None
[docs] @validator("FRONTEND_HOST", pre=True)
def assemble_cors_origins(
cls, v: Union[str, List[str]]
) -> Union[List[str], str]:
"""Convert a string array of the frontend origins
to an actual array
Args:
v (Union[str, List[str]]): _description_
Raises:
ValueError: _description_
Returns:
Union[List[str], str]: _description_
"""
if isinstance(v, str) and not v.startswith("["):
return [i.strip() for i in v.split(",")]
elif isinstance(v, (list, str)):
return v
raise ValueError(v)
[docs] class Config:
"""Pydantic config class."""
env_file: str = "./src/config/.env"
[docs] def set_envvar(self):
"""Temporarily set environment variables.
This change will not be permanent, so no
need to worry about overriding system
envvars.
"""
for key, value in self.dict(exclude_none=True).items():
# Save config to environment
os.environ[key] = str(value)
[docs]class DevConfig(GlobalConfig):
"""Development configuration.
Inherits from GlobalConfig, using
environment variables that start with
`DEV_` as the default values.
"""
[docs] class Config:
"""Pydantic config class.
Set the environment variable prefix
to `DEV_`.
"""
env_prefix: str = "DEV_"
[docs]class StagingConfig(GlobalConfig):
"""Staging configuration.
Inherits from GlobalConfig, using
environment variables that start with
`STG_` as the default values.
"""
[docs] class Config:
"""Pydantic config class.
Set the environment variable prefix
to `STG_`.
"""
env_prefix: str = "STG_"
[docs]class ProductionConfig(GlobalConfig):
"""Production configuration.
Inherits from GlobalConfig, using
environment variables that start with
`PROD_` as the default values.
"""
[docs] class Config:
"""Pydantic config class."""
env_prefix: str = "PROD_"
[docs]class TestingConfig(GlobalConfig):
"""Testing configuration used for unit tests.
Inherits from GlobalConfig, using
environment variables that start with
`TEST_` as the default values.
"""
[docs] class Config:
"""Pydantic config class."""
env_prefix: str = "TEST_"
[docs]class FactoryConfig:
"""Return config instance based on `ENV_STATE` variable"""
def __init__(self, env_state: Optional[str]):
self.env_state = env_state
def __call__(self):
if self.env_state == Environment.DEV:
return DevConfig()
elif self.env_state == Environment.STG:
return StagingConfig()
elif self.env_state == Environment.PROD:
return ProductionConfig()
elif self.env_state == Environment.TEST:
return TestingConfig()
else:
raise ValueError(f"Unsupported config: {self.env_state}")
ENV_STATE = GlobalConfig().ENV_STATE # Based on environment variables
config: GlobalConfig = FactoryConfig(
ENV_STATE
)() # Initialize config based on ENV_STATE
if config is not None:
# Set environment variables based on config
# this is useful for clearml credentials
# where the api key needs to be set as an
# environment variable
config.set_envvar()