Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ releases when needed to establish a durable public contract.

### Fixed

- Site variables moved from `group_vars/all.yml` to `group_vars/all/main.yml` in the
example inventory, configurator, and docs. Ansible loads only the `group_vars/all/`
directory when both exist, so the old file (callsign, grid, timezone, and other
site values) was silently ignored. Existing local inventories must move the file:
`mv inventory/local/group_vars/all.yml inventory/local/group_vars/all/main.yml`.
`site.yml`, `bootstrap.yml`, and `network.yml` now fail fast while the old file
is present next to `group_vars/all/`.
- `digital/ft8web`: added the missing nginx template that prevented the role
from deploying.
- `scripts/qa.py` now fails when a role task references a template or file the
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ cp -R inventory/example inventory/local
Then edit:

- `inventory/local/hosts.yml` for your real hosts;
- `inventory/local/group_vars/all.yml` for site-wide identity and defaults;
- `inventory/local/group_vars/all/main.yml` for site-wide identity and defaults;
- `inventory/local/group_vars/all/vault.yml` for encrypted site secrets;
- `inventory/local/host_vars/<hostname>.yml` for attached hardware and per-node role settings.

Expand Down
2 changes: 1 addition & 1 deletion docs/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ The common split is:
inventory/local/hosts.yml
Which Linux nodes exist and how Ansible reaches them.

inventory/local/group_vars/all.yml
inventory/local/group_vars/all/main.yml
Site-wide defaults such as callsign, timezone, event name.

inventory/local/host_vars/<hostname>.yml
Expand Down
13 changes: 10 additions & 3 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,23 @@ Then:

1. Edit `inventory/local/hosts.yml` and replace the example node with your real host(s).
2. Rename `inventory/local/host_vars/example-node.yml` so the filename matches the inventory hostname it configures.
3. Edit `inventory/local/group_vars/all.yml` with site-wide values such as callsign, grid, and timezone.
3. Edit `inventory/local/group_vars/all/main.yml` with site-wide values such as callsign, grid, and timezone.
4. Edit each host's `host_vars/<hostname>.yml` to describe attached hardware and host-specific role settings.
5. If a role needs credentials, create `inventory/local/group_vars/all/vault.yml` with Ansible Vault.
6. Test the inventory with `ansible all -m ping`.

Site variables live in `group_vars/all/main.yml`, inside the same directory as the
Vault file. When both a `group_vars/all.yml` file and a `group_vars/all/` directory
exist, Ansible loads only the directory and silently ignores the file. Inventories
created before this layout should move their site file into `group_vars/all/`;
`playbooks/site.yml`, `playbooks/bootstrap.yml`, `playbooks/network.yml`, and
`scripts/configure.py` validation refuse to continue while the old file is present.

`inventory/local/` is ignored by Git and is the normal place for real deployment data.

## Secrets

Do not put real credentials in `group_vars/all.yml`, role defaults, README files, or the tracked example inventory.
Do not put real credentials in `group_vars/all/main.yml`, role defaults, README files, or the tracked example inventory.

Create an encrypted local vault:

Expand All @@ -53,7 +60,7 @@ HamStack uses these conceptual layers:
```text
role defaults
↓
site variables in inventory/local/group_vars/all.yml
site variables in inventory/local/group_vars/all/main.yml
↓
host variables in inventory/local/host_vars/<host>.yml
↓
Expand Down
2 changes: 1 addition & 1 deletion docs/first-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ hamstack_gatus_tls_host: hamstack-demo
hamstack_gatus_https_port: 8443
```

Edit `inventory/local/group_vars/all.yml` and set at least:
Edit `inventory/local/group_vars/all/main.yml` and set at least:

```yaml
hamstack_site_name: My HamStack
Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ At minimum:
2. set the node's real IP address or DNS name;
3. rename `inventory/local/host_vars/example-node.yml` to match your hostname;
4. set your callsign and timezone in
`inventory/local/group_vars/all.yml`;
`inventory/local/group_vars/all/main.yml`;
5. enable the role(s) you actually want in that host's variables.

Do not enable every role just because it exists.
Expand Down
2 changes: 1 addition & 1 deletion group_vars/all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
# Intentionally contains no site-specific values.
#
# HamStack operator configuration belongs under:
# inventory/local/group_vars/all.yml
# inventory/local/group_vars/all/main.yml
#
# Start by copying inventory/example to inventory/local.
13 changes: 13 additions & 0 deletions playbooks/bootstrap.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@
become: true

pre_tasks:
- name: Reject site variables that Ansible would ignore
# With both group_vars/all.yml and group_vars/all/ present, Ansible loads
# only the directory and silently drops the file.
ansible.builtin.assert:
that: >-
not ((inventory_dir ~ '/group_vars/all.yml') is file
and (inventory_dir ~ '/group_vars/all') is directory)
fail_msg: >-
{{ inventory_dir }}/group_vars/all.yml is ignored because
group_vars/all/ also exists. Move it to group_vars/all/main.yml.
quiet: true
run_once: true

- name: Check for optional HamStack Vault file
ansible.builtin.stat:
path: "{{ inventory_dir }}/group_vars/all/vault.yml"
Expand Down
13 changes: 13 additions & 0 deletions playbooks/network.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@
become: false

pre_tasks:
- name: Reject site variables that Ansible would ignore
# With both group_vars/all.yml and group_vars/all/ present, Ansible loads
# only the directory and silently drops the file.
ansible.builtin.assert:
that: >-
not ((inventory_dir ~ '/group_vars/all.yml') is file
and (inventory_dir ~ '/group_vars/all') is directory)
fail_msg: >-
{{ inventory_dir }}/group_vars/all.yml is ignored because
group_vars/all/ also exists. Move it to group_vars/all/main.yml.
quiet: true
run_once: true

- name: Check for optional HamStack Vault file
ansible.builtin.stat:
path: "{{ inventory_dir }}/group_vars/all/vault.yml"
Expand All @@ -27,16 +40,16 @@
fail_msg: >-
Set hamstack_network_platform to openwrt or routeros for each device.

- name: Apply OpenWrt-family network backend

Check warning on line 43 in playbooks/network.yml

View workflow job for this annotation

GitHub Actions / qa

role-name[path]

Avoid using paths when importing roles. (network/openwrt)

Check warning on line 43 in playbooks/network.yml

View workflow job for this annotation

GitHub Actions / qa

role-name[path]

Avoid using paths when importing roles. (network/openwrt)
ansible.builtin.include_role:
name: network/openwrt
vars:
hamstack_openwrt_enabled: true

Check warning on line 47 in playbooks/network.yml

View workflow job for this annotation

GitHub Actions / qa

var-naming[no-role-prefix]

Variables names from within roles should use openwrt_ as a prefix. (vars: hamstack_openwrt_enabled)

Check warning on line 47 in playbooks/network.yml

View workflow job for this annotation

GitHub Actions / qa

var-naming[no-role-prefix]

Variables names from within roles should use openwrt_ as a prefix. (vars: hamstack_openwrt_enabled)
when: hamstack_network_platform == 'openwrt'

- name: Apply RouterOS network backend

Check warning on line 50 in playbooks/network.yml

View workflow job for this annotation

GitHub Actions / qa

role-name[path]

Avoid using paths when importing roles. (network/routeros)

Check warning on line 50 in playbooks/network.yml

View workflow job for this annotation

GitHub Actions / qa

role-name[path]

Avoid using paths when importing roles. (network/routeros)
ansible.builtin.include_role:
name: network/routeros
vars:
hamstack_routeros_enabled: true

Check warning on line 54 in playbooks/network.yml

View workflow job for this annotation

GitHub Actions / qa

var-naming[no-role-prefix]

Variables names from within roles should use routeros_ as a prefix. (vars: hamstack_routeros_enabled)

Check warning on line 54 in playbooks/network.yml

View workflow job for this annotation

GitHub Actions / qa

var-naming[no-role-prefix]

Variables names from within roles should use routeros_ as a prefix. (vars: hamstack_routeros_enabled)
when: hamstack_network_platform == 'routeros'
13 changes: 13 additions & 0 deletions playbooks/site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@
become: true

pre_tasks:
- name: Reject site variables that Ansible would ignore
# With both group_vars/all.yml and group_vars/all/ present, Ansible loads
# only the directory and silently drops the file.
ansible.builtin.assert:
that: >-
not ((inventory_dir ~ '/group_vars/all.yml') is file
and (inventory_dir ~ '/group_vars/all') is directory)
fail_msg: >-
{{ inventory_dir }}/group_vars/all.yml is ignored because
group_vars/all/ also exists. Move it to group_vars/all/main.yml.
quiet: true
run_once: true

- name: Check for optional HamStack Vault file
ansible.builtin.stat:
path: "{{ inventory_dir }}/group_vars/all/vault.yml"
Expand Down
11 changes: 10 additions & 1 deletion scripts/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@
LOCAL = ROOT / "inventory" / "local"
HOSTS_FILE = LOCAL / "hosts.yml"
NETWORK_FILE = LOCAL / "network.yml"
SITE_FILE = LOCAL / "group_vars" / "all.yml"
VAULT_DIR = LOCAL / "group_vars" / "all"
# Site variables live inside group_vars/all/: when both group_vars/all.yml and
# group_vars/all/ exist, Ansible loads only the directory.
SITE_FILE = VAULT_DIR / "main.yml"
LEGACY_SITE_FILE = LOCAL / "group_vars" / "all.yml"
VAULT_FILE = VAULT_DIR / "vault.yml"
VAULT_TEMPLATE = VAULT_DIR / "vault.yml.example"
HOST_VARS = LOCAL / "host_vars"
Expand Down Expand Up @@ -657,6 +660,12 @@ def validate() -> int:
if "example-node" in hosts:
warnings.append("example-node is still present in the local inventory.")

if LEGACY_SITE_FILE.exists():
errors.append(
"inventory/local/group_vars/all.yml is ignored by Ansible next to group_vars/all/; "
"move it to inventory/local/group_vars/all/main.yml."
)

site = load_yaml(SITE_FILE, {})
if site.get("hamstack_callsign") in {"", "N0CALL", None}:
warnings.append("hamstack_callsign is still unset/N0CALL.")
Expand Down
4 changes: 2 additions & 2 deletions scripts/generate-variable-reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ def infer_purpose(name: str, role: str) -> str:

def collect() -> dict[str, list[tuple[str, Any, str]]]:
groups: dict[str, list[tuple[str, Any, str]]] = {}
site = load_yaml(ROOT / "inventory" / "example" / "group_vars" / "all.yml")
groups["site/global"] = [(k, v, "inventory/example/group_vars/all.yml") for k, v in site.items() if k.startswith("hamstack_")]
site = load_yaml(ROOT / "inventory" / "example" / "group_vars" / "all" / "main.yml")
groups["site/global"] = [(k, v, "inventory/example/group_vars/all/main.yml") for k, v in site.items() if k.startswith("hamstack_")]
for defaults in sorted((ROOT / "roles").rglob("defaults/main.yml")):
role = defaults.parent.parent.relative_to(ROOT / "roles").as_posix()
data = load_yaml(defaults)
Expand Down
2 changes: 1 addition & 1 deletion scripts/init-local-inventory.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Created inventory/local from inventory/example.
Next:
1. Edit inventory/local/hosts.yml.
2. Rename inventory/local/host_vars/example-node.yml to match your host.
3. Edit inventory/local/group_vars/all.yml.
3. Edit inventory/local/group_vars/all/main.yml.
4. Create inventory/local/group_vars/all/vault.yml with ansible-vault if you need secrets.
Template: inventory/local/group_vars/all/vault.yml.example
5. Run: ansible all -m ping
Expand Down
3 changes: 3 additions & 0 deletions scripts/qa.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ def check_deprecated_paths() -> None:
forbidden = [
"inventory/local/group_vars/vault.yml",
"inventory/example/group_vars/vault.example.yml",
"inventory/example/group_vars/all.yml",
"examples/dc34-linbpq.yml",
"roles/services/hamstack-dashboard",
"hamstack_dashboard_",
Expand All @@ -251,6 +252,8 @@ def check_deprecated_paths() -> None:
deprecated_files = [
ROOT / "group_vars" / "all" / "vault.yml.example",
ROOT / "inventory" / "example" / "group_vars" / "vault.example.yml",
# Shadowed by group_vars/all/; site variables live in group_vars/all/main.yml.
ROOT / "inventory" / "example" / "group_vars" / "all.yml",
ROOT / "roles" / "packet" / "graywolf" / "main.yml",
]
for path in deprecated_files:
Expand Down
Loading