commit ce5287f3a7a88656692364a927ef3338b97bfd74
parent 16e6cbf2f92b3dc229172821fbee14f04bf1c208
Author: Asher Morgan <asher@ashermorgan.net>
Date: Tue, 8 Sep 2026 10:17:00 -0700
Implement AOH_MAX_RUNNERS option
Diffstat:
2 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/README.md b/README.md
@@ -58,6 +58,9 @@ environment variables or a `.env` file.
behind a proxy. Defaults to the origin on which requests for `/run.py` are
received.
+- `AOH_MAX_RUNNERS`: The maximum number of concurrent runners, or `0` for no
+ limit. Defaults to `0`.
+
- `AOH_PASSWORDS_FILE`: The file containing passwords for password-protected
playbooks (see below). Defaults to `/aoh/passwords.yml` when running the
Docker image and `./passwords.yml` otherwise.
diff --git a/aoh/server.py b/aoh/server.py
@@ -7,6 +7,8 @@ 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
@@ -53,6 +55,9 @@ 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:
+ return { 'err': 'No runners available.' }, 503
+
exp_pw_types = get_required_passwords(playbook, request.json['args'])
act_pws = request.json.get('passwords', {})
missing_pw_types = [pw_type for pw_type in exp_pw_types if pw_type not in