enroll/enroll/jinjaturtle.py
Miguel Jacq 6a36a9d2d5
Remote mode and dangerous flag, other tweaks
* Add remote mode for harvesting a remote machine via a local workstation (no need to install enroll remotely)
   Optionally use `--no-sudo` if you don't want the remote user to have passwordless sudo when conducting the
   harvest, albeit you'll end up with less useful data (same as if running `enroll harvest` on a machine without
   sudo)
 * Add `--dangerous` flag to capture even sensitive data (use at your own risk!)
 * Do a better job at capturing other config files in `/etc/<package>/` even if that package doesn't normally
   ship or manage those files.
2025-12-17 17:02:16 +11:00

83 lines
2.3 KiB
Python

from __future__ import annotations
import re
import shutil
import subprocess # nosec
import tempfile
from dataclasses import dataclass
from pathlib import Path
from typing import Optional
SUPPORTED_EXTS = {".ini", ".json", ".toml", ".yaml", ".yml", ".xml"}
@dataclass(frozen=True)
class JinjifyResult:
template_text: str
vars_text: str # YAML mapping text (no leading --- expected)
def find_jinjaturtle_cmd() -> Optional[str]:
"""Return the executable path for jinjaturtle if found on PATH."""
return shutil.which("jinjaturtle")
def can_jinjify_path(path: str) -> bool:
p = Path(path)
return p.suffix.lower() in SUPPORTED_EXTS
def run_jinjaturtle(
jt_exe: str,
src_path: str,
*,
role_name: str,
force_format: Optional[str] = None,
) -> JinjifyResult:
"""
Run jinjaturtle against src_path and return (template, defaults-yaml).
Uses tempfiles and captures outputs.
jinjaturtle CLI:
jinjaturtle <config> -r <role> [-f <format>] [-d <defaults-output>] [-t <template-output>]
"""
src = Path(src_path)
if not src.is_file():
raise FileNotFoundError(src_path)
with tempfile.TemporaryDirectory(prefix="enroll-jt-") as td:
td_path = Path(td)
defaults_out = td_path / "defaults.yml"
template_out = td_path / "template.j2"
cmd = [
jt_exe,
str(src),
"-r",
role_name,
"-d",
str(defaults_out),
"-t",
str(template_out),
]
if force_format:
cmd.extend(["-f", force_format])
p = subprocess.run(cmd, text=True, capture_output=True) # nosec
if p.returncode != 0:
raise RuntimeError(
"jinjaturtle failed for %s (role=%s)\ncmd=%r\nstdout=%s\nstderr=%s"
% (src_path, role_name, cmd, p.stdout, p.stderr)
)
vars_text = defaults_out.read_text(encoding="utf-8").strip()
template_text = template_out.read_text(encoding="utf-8")
# jinjaturtle outputs a YAML mapping; strip leading document marker if present
if vars_text.startswith("---"):
vars_text = "\n".join(vars_text.splitlines()[1:]).lstrip()
return JinjifyResult(
template_text=template_text, vars_text=vars_text.rstrip() + "\n"
)