Files
nixos-servers/scripts/create-host/generators.py
Torjus Håkestad 408554b477
Some checks failed
Run nix flake check / flake-check (push) Failing after 1m50s
Run nix flake check / flake-check (pull_request) Failing after 1m49s
scripts: add create-host tool for automated host configuration generation
Implements Phase 2 of the automated deployment pipeline.

This commit adds a Python CLI tool that automates the creation of NixOS host
configurations, eliminating manual boilerplate and reducing errors.

Features:
- Python CLI using typer framework with rich terminal UI
- Comprehensive validation (hostname format/uniqueness, IP subnet/uniqueness)
- Jinja2 templates for NixOS configurations
- Automatic updates to flake.nix and terraform/vms.tf
- Support for both static IP and DHCP configurations
- Dry-run mode for safe previews
- Packaged as Nix derivation and added to devShell

Usage:
  create-host --hostname myhost --ip 10.69.13.50/24

The tool generates:
- hosts/<hostname>/default.nix
- hosts/<hostname>/configuration.nix
- Updates flake.nix with new nixosConfigurations entry
- Updates terraform/vms.tf with new VM definition

All generated configurations include full system imports (monitoring, SOPS,
autoupgrade, etc.) and are validated with nix flake check and tofu validate.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-01 02:27:57 +01:00

89 lines
3.2 KiB
Python

"""File generation using Jinja2 templates."""
import sys
from pathlib import Path
from jinja2 import Environment, BaseLoader, TemplateNotFound
from models import HostConfig
class PackageTemplateLoader(BaseLoader):
"""Custom Jinja2 loader that works with both dev and installed packages."""
def __init__(self):
# Try to find templates in multiple locations
self.template_dirs = []
# Location 1: Development (scripts/create-host/templates)
dev_dir = Path(__file__).parent / "templates"
if dev_dir.exists():
self.template_dirs.append(dev_dir)
# Location 2: Installed via Nix (../share/create-host/templates from bin dir)
# When installed via Nix, __file__ is in lib/python3.X/site-packages/
# and templates are in ../../../share/create-host/templates
for site_path in sys.path:
site_dir = Path(site_path)
# Try to find the Nix store path
if "site-packages" in str(site_dir):
# Go up to the package root (e.g., /nix/store/xxx-create-host-0.1.0)
pkg_root = site_dir.parent.parent.parent
share_templates = pkg_root / "share" / "create-host" / "templates"
if share_templates.exists():
self.template_dirs.append(share_templates)
# Location 3: Fallback - sys.path templates
for site_path in sys.path:
site_templates = Path(site_path) / "templates"
if site_templates.exists():
self.template_dirs.append(site_templates)
def get_source(self, environment, template):
for template_dir in self.template_dirs:
template_path = template_dir / template
if template_path.exists():
mtime = template_path.stat().st_mtime
source = template_path.read_text()
return source, str(template_path), lambda: mtime == template_path.stat().st_mtime
raise TemplateNotFound(template)
def generate_host_files(config: HostConfig, repo_root: Path) -> None:
"""
Generate host configuration files from templates.
Args:
config: Host configuration
repo_root: Path to repository root
"""
# Setup Jinja2 environment with custom loader
env = Environment(
loader=PackageTemplateLoader(),
trim_blocks=True,
lstrip_blocks=True,
)
# Create host directory
host_dir = repo_root / "hosts" / config.hostname
host_dir.mkdir(parents=True, exist_ok=True)
# Generate default.nix
default_template = env.get_template("default.nix.j2")
default_content = default_template.render(hostname=config.hostname)
(host_dir / "default.nix").write_text(default_content)
# Generate configuration.nix
config_template = env.get_template("configuration.nix.j2")
config_content = config_template.render(
hostname=config.hostname,
domain=config.domain,
nameservers=config.nameservers,
is_static_ip=config.is_static_ip,
ip=config.ip,
gateway=config.gateway,
state_version=config.state_version,
)
(host_dir / "configuration.nix").write_text(config_content)