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