Research-Stack/4-Infrastructure/auto/lib/config.py
2026-05-25 16:24:21 -05:00

62 lines
1.9 KiB
Python

#!/usr/bin/env python3
"""Configuration loader for infra_controller. Parses YAML, resolves paths."""
from __future__ import annotations
import os
from pathlib import Path
from typing import Any
CONFIG_DIR = Path(__file__).resolve().parent.parent / "config"
def load_yaml(path: Path) -> dict[str, Any]:
"""Load YAML file, supporting both PyYAML and syck."""
try:
import yaml
with open(path) as fh:
return yaml.safe_load(fh) or {}
except ImportError:
pass
# pyyaml not available? try syck (python3-syck on Debian)
try:
import syck
return syck.load(open(path, 'rb').read()) or {}
except ImportError:
raise ImportError("No YAML library available. Install python3-pyyaml or python3-syck")
def resolve_env_vars(data: dict) -> dict:
"""Recursively resolve ${VAR} in string values."""
if isinstance(data, dict):
return {k: resolve_env_vars(v) for k, v in data.items()}
if isinstance(data, list):
return [resolve_env_vars(v) for v in data]
if isinstance(data, str) and "${" in data:
import re
def _repl(m):
return os.environ.get(m.group(1), "")
return re.sub(r'\$\{([^}]+)\}', _repl, data)
return data
def load_config(path: Path | None = None) -> dict[str, Any]:
"""Load and process the main config file."""
if path is None:
path = CONFIG_DIR / "nodes.yaml"
raw = load_yaml(path)
config = resolve_env_vars(raw)
# Flatten nodes dict for convenience
node_entries = config.get("nodes", {})
flat_nodes = {}
for name, cfg in node_entries.items():
flat_nodes[name] = {
"ip": cfg["tailscale_ip"],
"ssh_target": cfg.get("ssh_target", f"root@{cfg['tailscale_ip']}"),
"roles": cfg.get("roles", []),
"probes": cfg.get("probes", ["system"]),
"optional": cfg.get("optional", False),
}
config["nodes"] = flat_nodes
return config