Adding testing section to README

This commit is contained in:
Ian Roddis
2026-03-23 15:18:24 -03:00
parent 0bf5dd0024
commit 34b9b21950
+93 -5
View File
@@ -10,6 +10,7 @@ Sovereign is an Ansible project that deploys a complete self-hosted infrastructu
- [New Tenant Setup](#new-tenant-setup) - [New Tenant Setup](#new-tenant-setup)
- [Configuration Reference](#configuration-reference) - [Configuration Reference](#configuration-reference)
- [Deployment](#deployment) - [Deployment](#deployment)
- [Testing](#testing)
- [Maintenance](#maintenance) - [Maintenance](#maintenance)
- [Architecture Notes](#architecture-notes) - [Architecture Notes](#architecture-notes)
@@ -41,8 +42,8 @@ Sovereign is an Ansible project that deploys a complete self-hosted infrastructu
**Control machine** (where you run Ansible): **Control machine** (where you run Ansible):
- Python 3.9+ - Python 3.9+
- Ansible 8+ (`pip install ansible`) - Ansible 8+ — installed via `pip install -r requirements.txt` (see [Installing Dependencies](#installing-dependencies))
- Ansible collections (see [Installing Collections](#installing-collections)) - Ansible collections (see [Installing Dependencies](#installing-dependencies))
**Target host**: **Target host**:
@@ -51,13 +52,18 @@ Sovereign is an Ansible project that deploys a complete self-hosted infrastructu
- Ports 80, 443, and 51820/UDP open - Ports 80, 443, and 51820/UDP open
- DNS A records pointing `<domain>` and `*.<domain>` to the host IP - DNS A records pointing `<domain>` and `*.<domain>` to the host IP
### Installing Collections ### Installing Dependencies
Install Python packages (Ansible, Molecule, and linting tools) and Ansible collections:
```bash ```bash
pip install -r requirements.txt
ansible-galaxy collection install -r requirements.yml ansible-galaxy collection install -r requirements.yml
``` ```
Required collections: `community.docker >=3.0.0`, `community.general >=8.0.0`, `ansible.posix >=1.5.0`. Python packages (`requirements.txt`): `ansible`, `molecule`, `ansible-lint`, `yamllint`.
Ansible collections (`requirements.yml`): `community.docker >=3.0.0`, `community.general >=8.0.0`, `ansible.posix >=1.5.0`.
--- ---
@@ -67,7 +73,8 @@ Required collections: `community.docker >=3.0.0`, `community.general >=8.0.0`, `
# 1. Clone the repo # 1. Clone the repo
git clone <repo-url> sovereign && cd sovereign git clone <repo-url> sovereign && cd sovereign
# 2. Install Ansible collections # 2. Install Python packages and Ansible collections
pip install -r requirements.txt
ansible-galaxy collection install -r requirements.yml ansible-galaxy collection install -r requirements.yml
# 3. Configure the target host # 3. Configure the target host
@@ -207,6 +214,19 @@ All variables live in `inventories/production/group_vars/all.yml`.
| `base_domain` | `example.com` | Root domain. All subdomains are derived from this. | | `base_domain` | `example.com` | Root domain. All subdomains are derived from this. |
| `sovereign_base_dir` | `/opt/sovereign` | Base path on the target host for all service data. | | `sovereign_base_dir` | `/opt/sovereign` | Base path on the target host for all service data. |
### Branding
These variables apply consistent tenant branding across all services that support it. Services apply branding via environment variables, config file templates, or post-deploy API calls (e.g. Nextcloud `occ`, Authentik blueprints).
| Variable | Default | Description |
|----------|---------|-------------|
| `tenant_name` | `Example Corp` | Display name shown in service UIs, email subjects, and page titles. |
| `tenant_logo_local_path` | `""` | Path to a logo image on the Ansible control machine (PNG recommended). Leave empty to use each service's default logo. Example: `files/logo.png`. |
| `tenant_primary_color` | `#2563eb` | Primary brand colour (hex). Used for backgrounds, buttons, and highlights. |
| `tenant_accent_color` | `#1e40af` | Secondary/accent colour (hex). |
Services with branding support: Authentik (title, colour, logo via blueprint), Element/Matrix (brand name, theme), Forgejo (app name, logo), Nextcloud (name, colour, logo via `occ`), Jitsi (app name, watermark), Roundcube (product name), Wazuh dashboard (title).
### Traefik (`common` role) ### Traefik (`common` role)
| Variable | Default | Description | | Variable | Default | Description |
@@ -413,6 +433,73 @@ ansible-lint
--- ---
## Testing
Each role has a [Molecule](https://ansible.readthedocs.io/projects/molecule/) test scenario under `roles/<role>/molecule/default/`. Tests run entirely on the local machine — no target host or Docker daemon required.
### What the tests cover
- **Directory creation** — all expected data directories are created with correct permissions.
- **Template rendering** — every Jinja2 template renders without errors and with all variables substituted (no unresolved `{{ }}` in output files).
- **Config file content** — role-specific config files (Element `config.json`, Headscale `config.yaml`, Authentik branding blueprint, Roundcube `custom.inc.php`, Jitsi interface config, Wazuh dashboard YAML) contain the expected values.
- **Docker Compose structure** — `docker-compose.yml` references the correct image, Traefik routing labels, GELF logging address, and external network declaration.
- **Idempotency** — Molecule re-runs each role after converge and asserts zero changed tasks.
Docker/OS tasks (container start, `apt`, `systemd`, `sysctl`, health checks) are skipped during tests via the `molecule_test_mode` variable, which defaults to `false` and has no effect on real deployments.
### Install test dependencies
```bash
pip install -r requirements.txt
ansible-galaxy collection install -r requirements.yml
```
### Run tests for a single role
```bash
cd roles/authentik
molecule test
```
`molecule test` runs the full lifecycle: dependency → converge → idempotency check → verify → cleanup.
For a faster iteration loop during development:
```bash
# Apply the role and run assertions (skip create/destroy lifecycle)
molecule converge && molecule verify
# Clean up temp files when done
molecule destroy
```
### Run tests for all roles
```bash
for role in roles/*/; do
echo "=== Testing $role ==="
(cd "$role" && molecule test)
done
```
### Lint
```bash
ansible-lint # Ansible best-practice checks across all roles
yamllint . # YAML formatting checks
```
Both tools are configured via `.ansible-lint` and `.yamllint` at the repo root. The ansible-lint config mocks Docker and system modules so linting works without a live environment.
### Adding tests for a new role
1. Create `roles/<service>/molecule/default/` with `molecule.yml`, `converge.yml`, and `verify.yml` following the pattern of an existing simple role (e.g. `roles/website/molecule/default/`).
2. Add the new role's variables to `molecule/shared/vars.yml`.
3. Add `when: not (molecule_test_mode | default(false))` to any tasks that call `community.docker.docker_compose_v2`, `ansible.builtin.uri` (health checks), or `ansible.builtin.command` (docker exec).
4. Add the same guard to the role's restart handler in `handlers/main.yml`.
---
## Maintenance ## Maintenance
### Updating a service ### Updating a service
@@ -488,6 +575,7 @@ Follow the pattern used by existing roles:
3. Add the role to `playbooks/site.yml` with an appropriate tag. 3. Add the role to `playbooks/site.yml` with an appropriate tag.
4. Attach the container to the `sovereign` Docker network and add Traefik labels for routing. 4. Attach the container to the `sovereign` Docker network and add Traefik labels for routing.
5. Add `logging: driver: gelf` with `gelf-address: "udp://{{ graylog_host }}:{{ graylog_gelf_port }}"` to ship logs. 5. Add `logging: driver: gelf` with `gelf-address: "udp://{{ graylog_host }}:{{ graylog_gelf_port }}"` to ship logs.
6. Add a Molecule scenario — see [Adding tests for a new role](#adding-tests-for-a-new-role).
--- ---