atr/config.py (83 lines of code) (raw):

# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. import enum import os from typing import Final import decouple _MB: Final = 1024 * 1024 _GB: Final = 1024 * _MB class AppConfig: APP_HOST = decouple.config("APP_HOST", default="localhost") SSH_HOST = decouple.config("SSH_HOST", default="0.0.0.0") SSH_PORT = decouple.config("SSH_PORT", default=2222, cast=int) PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) STATE_DIR = decouple.config("STATE_DIR", default=os.path.join(PROJECT_ROOT, "state")) DEBUG = False TEMPLATES_AUTO_RELOAD = False USE_BLOCKBUSTER = False FINISHED_STORAGE_DIR = os.path.join(STATE_DIR, "finished") UNFINISHED_STORAGE_DIR = os.path.join(STATE_DIR, "unfinished") SQLITE_DB_PATH = decouple.config("SQLITE_DB_PATH", default="atr.db") # Apache RAT configuration APACHE_RAT_JAR_PATH = decouple.config("APACHE_RAT_JAR_PATH", default="/opt/tools/apache-rat-0.16.1.jar") # Maximum size limit for archive extraction MAX_EXTRACT_SIZE: int = decouple.config("MAX_EXTRACT_SIZE", default=2 * _GB, cast=int) # Chunk size for reading files during extraction EXTRACT_CHUNK_SIZE: int = decouple.config("EXTRACT_CHUNK_SIZE", default=4 * _MB, cast=int) # FIXME: retrieve the list of admin users from LDAP or oath session / isRoot ADMIN_USERS = frozenset( { "cwells", "dfoulks", "fluxo", "gmcdonald", "humbedooh", "sbp", "tn", "wave", } ) class DebugConfig(AppConfig): DEBUG = True TEMPLATES_AUTO_RELOAD = True USE_BLOCKBUSTER = False class Mode(enum.Enum): Debug = "Debug" Production = "Production" Profiling = "Profiling" _global_mode: Mode | None = None class ProductionConfig(AppConfig): ... class ProfilingConfig(AppConfig): DEBUG = False TEMPLATES_AUTO_RELOAD = False USE_BLOCKBUSTER = True # Load all possible configurations _CONFIG_DICT: Final = { Mode.Debug: DebugConfig, Mode.Production: ProductionConfig, Mode.Profiling: ProfilingConfig, } def get() -> type[AppConfig]: try: config = _CONFIG_DICT[get_mode()] except KeyError: exit("Error: Invalid <mode>. Expected values [Debug, Production, Profiling].") absolute_paths = [ (config.PROJECT_ROOT, "PROJECT_ROOT"), (config.STATE_DIR, "STATE_DIR"), (config.FINISHED_STORAGE_DIR, "FINISHED_STORAGE_DIR"), (config.UNFINISHED_STORAGE_DIR, "UNFINISHED_STORAGE_DIR"), ] relative_paths = [ (config.SQLITE_DB_PATH, "SQLITE_DB_PATH"), ] for path, name in absolute_paths: if not path.startswith("/"): raise RuntimeError(f"{name} must be an absolute path") for path, name in relative_paths: if path.startswith("/"): raise RuntimeError(f"{name} must be a relative path") return config def get_mode() -> Mode: global _global_mode if _global_mode is None: if decouple.config("PROFILING", default=False, cast=bool): _global_mode = Mode.Profiling elif decouple.config("PRODUCTION", default=False, cast=bool): _global_mode = Mode.Production else: _global_mode = Mode.Debug return _global_mode