commit 2d99a91daee2903c47f710785649070c8c6106e5
parent ad01caecedc0faeab421ebca0f0ed8d06ca81a7b
Author: Asher Morgan <asher@ashermorgan.net>
Date: Sun, 30 Aug 2026 14:13:21 -0700
Improve docker image
The docker image should now "just work" when the ./demo directory is
mounted at /aoh. This commit updates config defaults inside the docker
image, expands the ./demo example files, and fixes a config loading bug.
Diffstat:
9 files changed, 87 insertions(+), 14 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,6 +1,6 @@
-.env
-playbooks.yml
-passwords.yml
-*.pyc
__pycache__
.venv
+
+/.env
+/passwords.yml
+/playbooks.yml
diff --git a/Dockerfile b/Dockerfile
@@ -7,5 +7,11 @@ RUN python3 -m pip install --no-cache-dir -r requirements.txt
COPY aoh aoh
+RUN << EOF
+echo 'AOH_PASSWORDS_FILE=/aoh/passwords.yml' >> .env
+echo 'AOH_PLAYBOOKS_FILE=/aoh/playbooks.yml' >> .env
+EOF
+
EXPOSE 8000
+
CMD ["gunicorn", "-b", "0.0.0.0:8000", "aoh:create_app()"]
diff --git a/README.md b/README.md
@@ -24,15 +24,23 @@ new machines, anytime, anywhere.
| Connection direction | Server → client | Client → server | Client → server |
-## Setup
+## Getting Started
-1. Install python dependencies: `pip install -r requirements.txt`
+The AoH server is packaged as a Docker image. You can quickly try it out with
+the following commands:
-2. Create `.env`, `playbooks.yml`, and `passwords.yml` files as described below
+```sh
+# Build and run the server docker image:
+docker build -t aoh .
+docker run --rm --detach --name aoh -p 8000:8000 -v ./demo:/aoh aoh
-3. Start AoH development server: `python -m aoh`
+# Run the client CLI (the AoH password is "hunter2")
+curl -s 127.0.0.1:8000/run | python3
+curl -s 127.0.0.1:8000/run | python3 - --help
-4. Run AoH client: `curl -s localhost:5000/run | python3 - --help`
+# Stop the server docker container
+docker stop aoh
+```
## Configuration
@@ -48,13 +56,15 @@ environment variables or a `.env` file.
received.
- `AOH_PASSWORDS_FILE`: The file containing passwords for password-protected
- playbooks (see below). Defaults to `./passwords.yml`.
+ playbooks (see below). Defaults to `/aoh/passwords.yml` when running the
+ Docker image and `./passwords.yml` otherwise.
- `AOH_PLAYBOOKS_DIR`: The default directory to search for playbooks in.
Defaults to the directory containing `AOH_PLAYBOOKS_FILE`.
- `AOH_PLAYBOOKS_FILE`: The file containing playbook configuration (see below).
- Defaults to `./playbooks.yml`.
+ Defaults to `/aoh/playbooks.yml` when running the Docker image and
+ `./playbooks.yml` otherwise.
### Playbooks
@@ -62,15 +72,17 @@ environment variables or a `.env` file.
AoH only executes playbooks that are listed in the `$AOH_PLAYBOOKS_FILE` file,
which must have the following structure:
+<!-- EXAMPLE COPIED FROM demo/playbooks.yml: -->
+
```yml
main.yml: # The playbook name
# The path to the playbook, relative to $AOH_PLAYBOOKS_DIR. Defaults to the
# playbook name.
- path: demo/playbook.yml
+ path: playbooks/playbook.yml
# The path to an associated Ansible config file, if one exists.
- config: demo/ansible.cfg
+ config: playbooks/ansible.cfg
# The hostname to assign to clients. If omitted or null, each client is
# assigned the hostname that it reports for itself.
@@ -120,6 +132,8 @@ main.yml: # The playbook name
Passwords for password-protected playbooks must be specified as bcrypt hashes in
the `$AOH_PASSWORDS_FILE` file. For example:
+<!-- EXAMPLE COPIED FROM demo/passwords.yml: -->
+
```
# Require the password "hunter2" to execute the main.yml playbook
main.yml: $2a$14$LfC6Bcczc0WM.OObqgQPYeVvYa10g1Z4Z8C.eERyd.GarPmNa5/ve
diff --git a/aoh/__init__.py b/aoh/__init__.py
@@ -1,9 +1,11 @@
from dotenv import load_dotenv
+# Load config before importing app
+load_dotenv()
+
from .server import app, scheduler
def create_app():
- load_dotenv()
scheduler.start()
return app
diff --git a/demo/Dockerfile b/demo/client.Dockerfile
diff --git a/demo/passwords.yml b/demo/passwords.yml
@@ -0,0 +1,2 @@
+# Require the password "hunter2" to execute the main.yml playbook
+main.yml: $2a$14$LfC6Bcczc0WM.OObqgQPYeVvYa10g1Z4Z8C.eERyd.GarPmNa5/ve
diff --git a/demo/playbooks.yml b/demo/playbooks.yml
@@ -0,0 +1,49 @@
+main.yml: # The playbook name
+
+ # The path to the playbook, relative to $AOH_PLAYBOOKS_DIR. Defaults to the
+ # playbook name.
+ path: playbooks/playbook.yml
+
+ # The path to an associated Ansible config file, if one exists.
+ config: playbooks/ansible.cfg
+
+ # The hostname to assign to clients. If omitted or null, each client is
+ # assigned the hostname that it reports for itself.
+ host: my-aoh-host
+
+ # The groups to assign to clients, in addition to the aoh group and any groups
+ # defined in inventory files.
+ groups:
+ - group1
+ - group2
+
+ # Whether to require a password to execute the playbook. Passwords must be
+ # specified separately in the $AOH_PASSWORDS_FILE file (see below). Defaults
+ # to false.
+ password: true
+
+ # Additional raw ansible-playbook options
+ cmdline: '--limit aoh' # only run tasks for the current client
+
+ # Whether to print Ansible output on the client. Defaults to true.
+ output: true
+
+ # The ansible-playbooks options that users are allowed to invoke. Short and
+ # long option forms must be specified separately. A reasonable set of safe
+ # options are allowed by default, see aoh/security.py.
+ allow_opts:
+ - '--become-method'
+
+ # A list of ansible-playbook options that users are not allowed to invoke.
+ # This overrides the default allowed options and the options specified in
+ # allow_opts. Some options are always blocked, see aoh/security.py.
+ block_opts:
+ - '--list-tags'
+
+ # Whether to allow clients to use Jinja expressions in ansible-playbook
+ # options. Defaults to false.
+ jinja: true
+
+ # The description displayed on the playbook web page. Defaults to null, which
+ # disables the playbook web page.
+ web_description: 'Run the main.yml playbook with a single command:'
diff --git a/demo/ansible.cfg b/demo/playbooks/ansible.cfg
diff --git a/demo/playbook.yml b/demo/playbooks/playbook.yml