aoh

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

commit 555df61ee112a712a11099699c348ac063360be9
parent 9f92f5436ed2ae11c21fc3a07323ecabe6b08568
Author: Asher Morgan <asher@ashermorgan.net>
Date:   Tue,  8 Sep 2026 19:27:56 -0700

Extract common config parsing logic

Diffstat:
Maoh/runner.py | 49++++++++++++++++++++++++++++++++++---------------
Maoh/security.py | 23+++++------------------
2 files changed, 39 insertions(+), 33 deletions(-)

diff --git a/aoh/runner.py b/aoh/runner.py @@ -1,3 +1,4 @@ +import ast import json import os import shlex @@ -24,6 +25,33 @@ class RunnerError(Exception): """Raised for AoH runner errors.""" +def get_config_values(config, keys): + """Query Ansible config option values.""" + + with tempfile.TemporaryDirectory(prefix='aoh-') as dir: + raw_config = ansible_runner.get_ansible_config( + 'dump', + config, + private_data_dir=dir, + quiet=True, + )[0] + + if not raw_config: + raise RunnerError(f'Failed to dump Ansible config from {config}') + + result = {} + for key in keys: + line = next(( + x for x in raw_config.split('\n') + if x.startswith(key + '(') + ), None) + if not line: + raise RunnerError(f'Ansible config key "{key}" not found') + result[key] = ast.literal_eval(line.split('=')[1]) + + return result + + class Runner: """An Ansible-over-HTTP playbook runner.""" @@ -77,30 +105,21 @@ class Runner: f.write(f'[aoh]\n{self.host} ansible_connection=aoh\n') f.writelines(f'[{g}]\n{self.host}\n' for g in self.playbook.groups) - raw_config = ansible_runner.get_ansible_config( - 'dump', + config = get_config_values( self.playbook.config, - 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') - ).split('= ')[1]) - inventory = eval(next( - x for x in raw_config.split('\n') - if x.startswith('DEFAULT_HOST_LIST') - ).split('= ')[1]) + ['DEFAULT_CONNECTION_PLUGIN_PATH', 'DEFAULT_HOST_LIST'] + ) env = { 'ANSIBLE_AOH_DIR': self._DIR, 'ANSIBLE_CONNECTION_PLUGINS': ':'.join( - [_CONNECTION_PLUGIN_DIR] + connection_plugins + [_CONNECTION_PLUGIN_DIR] + + config['DEFAULT_CONNECTION_PLUGIN_PATH'] ), 'ANSIBLE_CONFIG': self.playbook.config, 'ANSIBLE_FORCE_COLOR': '1', 'ANSIBLE_INVENTORY': ','.join( - [f'{self._DIR}/inventory.ini'] + inventory + [f'{self._DIR}/inventory.ini'] + config['DEFAULT_HOST_LIST'] ), } pw_prompt_answers = {} diff --git a/aoh/security.py b/aoh/security.py @@ -1,10 +1,8 @@ -import tempfile - -import ansible_runner import bcrypt import yaml from .config import AOH_PASSWORDS_FILE +from .runner import get_config_values _CLI_OPT_WHITELIST = [ # These options should be safe for clients to invoke @@ -92,21 +90,10 @@ def get_required_passwords(playbook, args): if opt in opts: pw_types.add(pw_type) - with tempfile.TemporaryDirectory(prefix='aoh-') as dir: - raw_config = ansible_runner.get_ansible_config( - 'dump', - playbook.config, - private_data_dir=dir, - quiet=True, - )[0] - - for opt, pw_type in _CONFIG_PASSWORD_OPTS.items(): - val = eval(next( - x for x in raw_config.split('\n') - if x.startswith(opt) - ).split('= ')[1]) - if val: - pw_types.add(pw_type) + opts = get_config_values(playbook.config, _CONFIG_PASSWORD_OPTS.keys()) + for opt, pw_type in _CONFIG_PASSWORD_OPTS.items(): + if opts[opt]: + pw_types.add(pw_type) return list(pw_types)