commit 9f92f5436ed2ae11c21fc3a07323ecabe6b08568
parent cf229951dbebaca448a6d2230def1234132ef404
Author: Asher Morgan <asher@ashermorgan.net>
Date: Tue, 8 Sep 2026 18:30:12 -0700
Extract all config logic to aoh/config.py
Diffstat:
6 files changed, 51 insertions(+), 18 deletions(-)
diff --git a/aoh/__init__.py b/aoh/__init__.py
@@ -1,8 +1,3 @@
-from dotenv import load_dotenv
-
-# Load config before importing app
-load_dotenv()
-
from .server import app, scheduler
diff --git a/aoh/config.py b/aoh/config.py
@@ -0,0 +1,39 @@
+import logging
+import os
+from urllib.parse import urlsplit
+
+from dotenv import load_dotenv
+
+logger = logging.getLogger(__name__)
+
+load_dotenv()
+
+AOH_DEBUG = (os.getenv('AOH_DEBUG', '0') == '1')
+
+try:
+ AOH_MAX_RUNNERS = int(os.getenv('AOH_MAX_RUNNERS', '0'))
+except ValueError:
+ logger.warning('$AOH_MAX_RUNNERS must be a non-negative integer')
+ AOH_MAX_RUNNERS = 0
+if AOH_MAX_RUNNERS < 0:
+ logger.warning('$AOH_MAX_RUNNERS must be a non-negative integer')
+ AOH_MAX_RUNNERS = 0
+
+AOH_ORIGIN = os.getenv('AOH_ORIGIN')
+if AOH_ORIGIN is not None:
+ try:
+ parts = urlsplit(AOH_ORIGIN)
+ if parts.scheme == '' or parts.path != '':
+ logger.warning('$AOH_ORIGIN is not a valid origin')
+ AOH_ORIGIN = None
+ except ValueError:
+ logger.warning('$AOH_ORIGIN is not a valid origin')
+ AOH_ORIGIN = None
+
+AOH_PLAYBOOKS_FILE = os.getenv('AOH_PLAYBOOKS_FILE', 'playbooks.yml')
+if not os.path.exists(AOH_PLAYBOOKS_FILE):
+ logger.warning('$AOH_PLAYBOOKS_FILE does not exist')
+
+AOH_PASSWORDS_FILE = os.getenv('AOH_PASSWORDS_FILE', 'passwords.yml')
+if not os.path.exists(AOH_PASSWORDS_FILE):
+ logger.warning('$AOH_PASSWORDS_FILE does not exist')
diff --git a/aoh/playbook.py b/aoh/playbook.py
@@ -2,8 +2,9 @@ import os
import yaml
-_PLAYBOOKS_FILE = os.getenv('AOH_PLAYBOOKS_FILE', 'playbooks.yml')
-_PLAYBOOKS_DIR = os.path.dirname(_PLAYBOOKS_FILE)
+from .config import AOH_PLAYBOOKS_FILE
+
+_PLAYBOOKS_DIR = os.path.dirname(AOH_PLAYBOOKS_FILE)
class PlaybookError(Exception):
@@ -57,7 +58,7 @@ class Playbook:
def get_playbook(name):
"""Lookup a playbook configuration."""
- with open(_PLAYBOOKS_FILE, 'r') as f:
+ with open(AOH_PLAYBOOKS_FILE, 'r') as f:
CONFIG = yaml.safe_load(f)
if name in CONFIG:
return Playbook(name, CONFIG[name])
diff --git a/aoh/runner.py b/aoh/runner.py
@@ -9,7 +9,7 @@ import uuid
import ansible_runner
-_AOH_DEBUG = (os.getenv('AOH_DEBUG', '0') == '1')
+from .config import AOH_DEBUG
_CONNECTION_PLUGIN_DIR = f'{os.path.dirname(__file__)}/connection_plugins/'
@@ -121,7 +121,7 @@ class Runner:
passwords=pw_prompt_answers,
playbook=self.playbook.path,
finished_callback=_finished_callback,
- quiet=not _AOH_DEBUG,
+ quiet=not AOH_DEBUG,
suppress_env_files=True,
)
diff --git a/aoh/security.py b/aoh/security.py
@@ -1,10 +1,11 @@
-import os
import tempfile
import ansible_runner
import bcrypt
import yaml
+from .config import AOH_PASSWORDS_FILE
+
_CLI_OPT_WHITELIST = [
# These options should be safe for clients to invoke
'--ask-become-pass', '-K',
@@ -43,8 +44,6 @@ _CONFIG_PASSWORD_OPTS = {
'DEFAULT_BECOME_ASK_PASS': 'become_password',
}
-_PASSWORDS_FILE = os.getenv('AOH_PASSWORDS_FILE', 'passwords.yml')
-
def _get_opts(args):
"""Identify options present in a list of CLI arguments."""
@@ -120,6 +119,6 @@ def validate_aoh_password(playbook, password):
if password is None:
return False
- with open(_PASSWORDS_FILE, 'r') as f:
+ with open(AOH_PASSWORDS_FILE, 'r') as f:
hash = yaml.safe_load(f).get(playbook.name)
return bcrypt.checkpw(password.encode(), hash.encode())
diff --git a/aoh/server.py b/aoh/server.py
@@ -3,12 +3,11 @@ import os
from flask import Flask, abort, render_template, request, session
from flask_apscheduler import APScheduler
+from .config import AOH_MAX_RUNNERS, AOH_ORIGIN
from .playbook import get_playbook
from .runner import Runner
from .security import *
-_MAX_RUNNERS = int(os.getenv('AOH_MAX_RUNNERS', '0'))
-
_RUNNERS = {}
# Every 60s, tear down runners that finished more than 60s ago
@@ -36,7 +35,7 @@ def gc():
@app.context_processor
def inject_stage_and_region():
return {
- 'AOH_ORIGIN': os.getenv('AOH_ORIGIN', request.host_url[:-1])
+ 'AOH_ORIGIN': AOH_ORIGIN or request.host_url[:-1]
}
@@ -55,7 +54,7 @@ def new_runner(path):
if not validate_args(playbook, request.json['args']):
return {'err': 'Bad or banned arguments passed.'}, 400
- if _MAX_RUNNERS != 0 and len(_RUNNERS) >= _MAX_RUNNERS:
+ if AOH_MAX_RUNNERS != 0 and len(_RUNNERS) >= AOH_MAX_RUNNERS:
return { 'err': 'No runners available.' }, 503
exp_pw_types = get_required_passwords(playbook, request.json['args'])