aoh

A tool for running Ansible playbooks with `curl <url> | python` commands
git clone https://git.ashermorgan.net/aoh/
Log | Files | Refs | README

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:
Maoh/__init__.py | 5-----
Aaoh/config.py | 39+++++++++++++++++++++++++++++++++++++++
Maoh/playbook.py | 7++++---
Maoh/runner.py | 4++--
Maoh/security.py | 7+++----
Maoh/server.py | 7+++----
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'])