From e20b85e00c2bdae486c794ecd6e7a357e9558a39 Mon Sep 17 00:00:00 2001 From: dw-0 Date: Sat, 4 Jul 2026 14:11:01 +0200 Subject: [PATCH] feat(tests): load live VM host/key from .env, keep inventory non-sensitive --- docs/live-testing.md | 27 +++++++++------ kiauh/live/inventory.py | 30 ++++++++++++++--- kiauh/live/inventory.yaml | 7 ++-- kiauh/live/tests/test_inventory.py | 54 ++++++++++++++++++++++++++++++ pyproject.toml | 2 +- requirements-dev.txt | 1 + 6 files changed, 104 insertions(+), 17 deletions(-) create mode 100644 kiauh/live/tests/test_inventory.py diff --git a/docs/live-testing.md b/docs/live-testing.md index a1a446f6..fa0e76e4 100644 --- a/docs/live-testing.md +++ b/docs/live-testing.md @@ -24,17 +24,24 @@ the VM in the inventory. ## Inventory -Edit `kiauh/live/inventory.yaml` or point to a custom file: +`kiauh/live/inventory.yaml` defines non-sensitive VM settings. The host and SSH +key path are read from environment variables or a `.env` file so they are not +committed. -```yaml -vms: - - name: debian12-kiauh - host: 192.168.122.10 - user: kiauh - key_file: ~/.ssh/kiauh_vm - os: debian-12 - domain: debian12-kiauh - snapshot: clean +Create `.env` in the project root: + +```bash +KIAUH_LIVE_DEBIAN12_KIAUH_HOST=192.168.122.10 +KIAUH_LIVE_DEBIAN12_KIAUH_KEY_FILE=/home/you/.ssh/kiauh_vm +``` + +Variable naming: `KIAUH_LIVE__HOST` and `KIAUH_LIVE__KEY_FILE`, +with the VM name uppercased and hyphens replaced by underscores. + +You can also point to a custom inventory file: + +```bash +export KIAUH_LIVE_INVENTORY=/path/to/inventory.yaml ``` ## Run Live Tests diff --git a/kiauh/live/inventory.py b/kiauh/live/inventory.py index 2a2c3776..281cbf25 100644 --- a/kiauh/live/inventory.py +++ b/kiauh/live/inventory.py @@ -15,6 +15,13 @@ from typing import List import yaml +try: + from dotenv import load_dotenv + + load_dotenv(Path(__file__).parents[2].joinpath(".env")) +except ImportError: + load_dotenv = None + DEFAULT_INVENTORY_PATH = Path(__file__).parent.joinpath("inventory.yaml") @@ -33,6 +40,11 @@ class InventoryError(Exception): pass +def _env_var_name(vm_name: str, suffix: str) -> str: + safe_name = vm_name.replace("-", "_").upper() + return f"KIAUH_LIVE_{safe_name}_{suffix.upper()}" + + def load_inventory(path: Path | None = None) -> List[VM]: inventory_path = Path(path or os.environ.get("KIAUH_LIVE_INVENTORY", DEFAULT_INVENTORY_PATH)) if not inventory_path.exists(): @@ -44,15 +56,25 @@ def load_inventory(path: Path | None = None) -> List[VM]: vms = [] for item in data["vms"]: - for required in ("name", "host", "user", "key_file", "os"): + for required in ("name", "user", "os"): if required not in item: raise InventoryError(f"VM '{item.get('name', '?')}' missing '{required}'") + + name = item["name"] + host = os.environ.get(_env_var_name(name, "host"), item.get("host")) + key_file = os.environ.get(_env_var_name(name, "key_file"), item.get("key_file")) + + if not host: + raise InventoryError(f"VM '{name}' missing 'host' (inventory or env)") + if not key_file: + raise InventoryError(f"VM '{name}' missing 'key_file' (inventory or env)") + vms.append( VM( - name=item["name"], - host=item["host"], + name=name, + host=host, user=item["user"], - key_file=item["key_file"], + key_file=key_file, os=item["os"], domain=item.get("domain"), snapshot=item.get("snapshot"), diff --git a/kiauh/live/inventory.yaml b/kiauh/live/inventory.yaml index 82f99def..ed71b972 100644 --- a/kiauh/live/inventory.yaml +++ b/kiauh/live/inventory.yaml @@ -1,8 +1,11 @@ vms: - name: debian12-kiauh - host: 192.168.122.10 + # Override host/key_file via environment variables or a .env file. + # KIAUH_LIVE_DEBIAN12_KIAUH_HOST + # KIAUH_LIVE_DEBIAN12_KIAUH_KEY_FILE + host: null user: kiauh - key_file: ~/.ssh/kiauh_vm + key_file: null os: debian-12 domain: debian12-kiauh snapshot: clean diff --git a/kiauh/live/tests/test_inventory.py b/kiauh/live/tests/test_inventory.py new file mode 100644 index 00000000..ab641a12 --- /dev/null +++ b/kiauh/live/tests/test_inventory.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +from pathlib import Path + +import pytest +from live.inventory import InventoryError, VM, load_inventory + + +class TestLoadInventory: + def test_loads_vm_from_inventory(self, tmp_path: Path) -> None: + inv = tmp_path / "inv.yaml" + inv.write_text( + "vms:\n" + " - name: debian12\n" + " host: 10.0.0.5\n" + " user: kiauh\n" + " key_file: /key\n" + " os: debian-12\n" + ) + vms = load_inventory(inv) + assert len(vms) == 1 + assert vms[0].host == "10.0.0.5" + assert vms[0].key_file == "/key" + + def test_env_overrides_host_and_key_file( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: + monkeypatch.setenv("KIAUH_LIVE_DEBIAN12_HOST", "192.168.1.50") + monkeypatch.setenv("KIAUH_LIVE_DEBIAN12_KEY_FILE", "/secret/key") + + inv = tmp_path / "inv.yaml" + inv.write_text( + "vms:\n" + " - name: debian12\n" + " host: 10.0.0.5\n" + " user: kiauh\n" + " key_file: /key\n" + " os: debian-12\n" + ) + vms = load_inventory(inv) + assert vms[0].host == "192.168.1.50" + assert vms[0].key_file == "/secret/key" + + def test_missing_host_raises(self, tmp_path: Path) -> None: + inv = tmp_path / "inv.yaml" + inv.write_text( + "vms:\n" + " - name: debian12\n" + " user: kiauh\n" + " key_file: /key\n" + " os: debian-12\n" + ) + with pytest.raises(InventoryError, match="missing 'host'"): + load_inventory(inv) diff --git a/pyproject.toml b/pyproject.toml index 797f9aa6..717a1365 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ requires-python = ">=3.8" [project.optional-dependencies] -dev=["ruff", "mypy", "pytest", "paramiko", "pytest-testinfra", "pyyaml"] +dev=["ruff", "mypy", "pytest", "paramiko", "pytest-testinfra", "pyyaml", "python-dotenv"] [tool.ruff] required-version = ">=0.9.10" diff --git a/requirements-dev.txt b/requirements-dev.txt index 812ca51f..a73d5b35 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,3 +4,4 @@ pytest paramiko pytest-testinfra pyyaml +python-dotenv