aoh

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

commit 72afad80bbff590c1490dceb20cf51fd88fe941d
parent bf584cd1a84b287d15704e68bbfe7b355d2ac165
Author: Asher Morgan <asher@ashermorgan.net>
Date:   Sun,  6 Sep 2026 18:38:33 -0700

Minor cleanup

Diffstat:
Maoh/connection_plugins/aoh.py | 2++
Maoh/playbook.py | 5++++-
Maoh/runner.py | 19+++++++------------
Maoh/security.py | 2+-
Maoh/server.py | 4++--
Maoh/static/copy.js | 2+-
6 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/aoh/connection_plugins/aoh.py b/aoh/connection_plugins/aoh.py @@ -10,6 +10,8 @@ DOCUMENTATION = """ description: The AoH runner directory type: string required: true + env: + - name: ANSIBLE_AOH_DIR vars: - name: ansible_aoh_dir aoh_timeout: diff --git a/aoh/playbook.py b/aoh/playbook.py @@ -23,7 +23,7 @@ class Playbook: 'password': bool, 'output': bool, 'jinja': bool, - 'allow_opts': list, # We'll just assume that elements are strings + 'allow_opts': list, 'block_opts': list, 'extra_args': list, 'web_description': str, @@ -33,6 +33,9 @@ class Playbook: if key in dict and type(dict[key]) != _type: raise PlaybookError(f'"{key}" field must be of type {_type}') + if _type is list and any(type(x) != str for x in dict[key]): + raise PlaybookError(f'"{key}" elements must be of type str') + self.name = name self.path = dict.get('path', name) self.config = dict.get('config', None) diff --git a/aoh/runner.py b/aoh/runner.py @@ -3,9 +3,9 @@ import os import shlex import shutil import tempfile +import threading import time -from threading import Lock -from uuid import uuid4 +import uuid import ansible_runner @@ -19,6 +19,7 @@ _PASSWORD_PROMPTS = { 'vault_password': '^Vault password:\\s*?$', } + class RunnerError(Exception): """Raised for AoH runner errors.""" @@ -29,13 +30,13 @@ class Runner: def __init__(self, playbook, host, args, passwords): """Create a new AoH runner.""" - self.id = str(uuid4()) # Runner ID (set to None after teardown) + self.id = str(uuid.uuid4()) # Runner ID (set to None after teardown) self.playbook = playbook self.host = self.playbook.host or host or 'aoh_node' self.args = args self.passwords = passwords - self._lock = Lock() # Used to protect all public methods + self._lock = threading.Lock() # Used to protect all public methods self._DIR = tempfile.mkdtemp(prefix='aoh-') self._LOGS_PATH = f'{self._DIR}/artifacts/{self.id}/stdout' @@ -82,7 +83,6 @@ class Runner: private_data_dir=self._DIR, quiet=True, )[0] - connection_plugins = eval(next( x for x in raw_config.split('\n') if x.startswith('DEFAULT_CONNECTION_PLUGIN_PATH') @@ -93,6 +93,7 @@ class Runner: ).split('= ')[1]) env = { + 'ANSIBLE_AOH_DIR': self._DIR, 'ANSIBLE_CONNECTION_PLUGINS': ':'.join( [_CONNECTION_PLUGIN_DIR] + connection_plugins ), @@ -102,9 +103,6 @@ class Runner: [f'{self._DIR}/inventory.ini'] + inventory ), } - vars = { - 'ansible_aoh_dir': self._DIR, - } pw_prompt_answers = {} for pw_type in self.passwords: pw_prompt_answers[_PASSWORD_PROMPTS[pw_type]] = \ @@ -119,7 +117,6 @@ class Runner: private_data_dir=self._DIR, ident=self.id, envvars=env, - extravars=vars, cmdline=cmdline, passwords=pw_prompt_answers, playbook=self.playbook.path, @@ -150,9 +147,7 @@ class Runner: with self._lock: if self.id is None: # Runner has already been torn down, probably due to timeout - return { - 'err': 'Client timeout', - } + return { 'err': 'Client timeout' } res = {} diff --git a/aoh/security.py b/aoh/security.py @@ -115,7 +115,7 @@ def validate_aoh_password(playbook, password): if not playbook.password: return True - if not password: + if password is None: return False with open(_PASSWORDS_FILE, 'r') as f: diff --git a/aoh/server.py b/aoh/server.py @@ -48,7 +48,7 @@ def install(): def new_runner(path): playbook = get_playbook(path) if not playbook: - return { 'err': f"Playbook not found: {path}" }, 400 + return { 'err': f'Playbook not found: {path}' }, 400 if not validate_args(playbook, request.json['args']): return { 'err': 'Bad or banned arguments passed.' }, 400 @@ -88,7 +88,7 @@ def runner_update(path, short_id): if not runner: return { 'err': 'Runner not found' }, 404 if runner.playbook.name != path: - return { 'err': f"Playbook not found: {path}" }, 400 + return { 'err': f'Playbook not found: {path}' }, 400 res = runner.process_message(request.json) diff --git a/aoh/static/copy.js b/aoh/static/copy.js @@ -18,6 +18,6 @@ function copy(cmd) { document.getElementById('copy').innerHTML = DONE_SVG; setTimeout(() => { document.getElementById('copy').innerHTML = COPY_SVG; - document.getElementById('copy').disabled = false; + document.getElementById('copy').disabled = false; }, 1000); }