aoh

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

commit ad83738738f59ac3ce5f10f3226eed82578c8449
parent ef5fe688ad7a9952b6104bdd3355673653f3dc47
Author: Asher Morgan <asher@ashermorgan.net>
Date:   Sat, 15 Aug 2026 19:30:59 -0700

Move client request processing into Runner class

Diffstat:
Minstall.py | 2+-
Mmain.py | 62++++++++++++++++++++++++++++++++++++++++----------------------
2 files changed, 41 insertions(+), 23 deletions(-)

diff --git a/install.py b/install.py @@ -65,7 +65,7 @@ def main(): print(res['logs'], end='') req = {} - if res['status'] not in ['started', 'running']: + if res['finished']: break elif 'exec' in res: req = exec(res['exec']) diff --git a/main.py b/main.py @@ -19,16 +19,19 @@ app.secret_key = os.urandom(16) class Runner: def __init__(self, config, playbook, host): self.id = str(uuid4()) - self.dir = tempfile.mkdtemp() + self._dir = tempfile.mkdtemp() + self._SENDBUF_PATH = f'{self._dir}/sendbuf' + self._RECVBUF_PATH = f'{self._dir}/recvbuf' + self._LOGS_PATH = f'{self._dir}/artifacts/{self.id}/stdout' - os.mkfifo(f'{self.dir}/sendbuf') - os.mkfifo(f'{self.dir}/recvbuf') + os.mkfifo(self._SENDBUF_PATH) + os.mkfifo(self._RECVBUF_PATH) self._start(config, playbook, host) - self.sendbuf = open(f'{self.dir}/sendbuf', 'r') - self.recvbuf = open(f'{self.dir}/recvbuf', 'w') - self.logs = open(f'{self.dir}/artifacts/{self.id}/stdout', 'r') + self._sendbuf = open(self._SENDBUF_PATH, 'r') + self._recvbuf = open(self._RECVBUF_PATH, 'w') + self._logs = open(self._LOGS_PATH, 'r') def _start(self, config, playbook, host): @@ -48,12 +51,12 @@ class Runner: 'ANSIBLE_CONFIG': config, } vars = { - 'ansible_http_runner': self.dir, + 'ansible_http_runner': self._dir, 'ansible_connection': 'http', } self.thread, self.runner = ansible_runner.run_async( - private_data_dir=self.dir, + private_data_dir=self._dir, ident=self.id, envvars=env, extravars=vars, @@ -67,13 +70,35 @@ class Runner: ) + def _runner_finished(self): + return self.runner.status not in ['started', 'running'] + + + def process_client_request(self, req): + res = {} + + # We check runner status first, in case new logs come in afterwards + res['finished'] = self._runner_finished() + res['logs'] = self._logs.read() + + if req: + self._recvbuf.write(json.dumps(req) + '\n') + self._recvbuf.flush() + + line = self._sendbuf.readline() + for key, val in json.loads(line or '{}').items(): + res[key] = val + + return res + + def teardown(self): self.thread.join() self.id = None - self.sendbuf.close() - self.recvbuf.close() - self.logs.close() - shutil.rmtree(self.dir) + self._sendbuf.close() + self._recvbuf.close() + self._logs.close() + shutil.rmtree(self._dir) @app.get('/install') @@ -96,18 +121,11 @@ def job_new(): def job_status(id): if id != session.get('runner'): abort(401) - runner = DATA[id] - - if request.json: - runner.recvbuf.write(json.dumps(request.json) + '\n') - runner.recvbuf.flush() - res = json.loads(runner.sendbuf.readline() or '{}') - res['status'] = runner.runner.status - res['logs'] = runner.logs.read() + res = DATA[id].process_client_request(request.json) - if runner.runner.status not in ['started', 'running']: - runner.teardown() + if res['finished']: + DATA[id].teardown() del DATA[id] return res