diff --git a/terraform/scripts/stage_talos_image.py b/terraform/scripts/stage_talos_image.py index 4f33377..dada3b3 100644 --- a/terraform/scripts/stage_talos_image.py +++ b/terraform/scripts/stage_talos_image.py @@ -4,10 +4,10 @@ import gzip import json import os import shutil +import ssl import subprocess import sys import tempfile -import ssl import time import urllib.error import urllib.parse @@ -79,41 +79,66 @@ def wait_for_task(base_url, token, node, upid): raise RuntimeError("timed out waiting for ISO download task to finish") +def detect_proxmox_iso_dir(storage): + candidates = [ + Path("/var/lib/vz/template/iso"), + Path("C:/var/lib/vz/template/iso"), + ] + for candidate in candidates: + if candidate.exists(): + return candidate + raise RuntimeError(f"unable to find ISO directory for storage '{storage}'") + + + def build_custom_iso(base_url, token, node, storage, source_filename, output_filename, network_config_path): - storage_path = f"/var/lib/vz/template/iso/{source_filename}" - output_path = f"/var/lib/vz/template/iso/{output_filename}" + iso_dir = detect_proxmox_iso_dir(storage) + storage_path = iso_dir / source_filename + output_path = iso_dir / output_filename network_config = Path(network_config_path).read_text(encoding="utf-8").strip() installer_meta = base64.b64encode(gzip.compress(f"0xa={network_config}".encode("utf-8"), compresslevel=9)).decode("ascii") - if shutil.which("docker") is None: - raise RuntimeError("docker is required to build Talos ISO with embedded META") + xorriso = shutil.which("xorriso") + if xorriso is None: + raise RuntimeError("xorriso is required to build Talos ISO with embedded META") with tempfile.TemporaryDirectory() as temp_dir: temp_path = Path(temp_dir) - script_path = temp_path / "build.sh" - script_path.write_text( - "#!/bin/sh\n" - "set -eu\n" - "apk add --no-cache xorriso >/dev/null\n" - "cp /input/source.iso /work/source.iso\n" - "xorriso -indev /work/source.iso -outdev /output/result.iso -boot_image any replay \\\n" - f" -append_partition 2 0x0 /dev/null \\\n" - f" -boot_image any keep \\\n" - f" -map /input/cmdline /cmdline\n", - encoding="utf-8", - ) - cmdline_path = temp_path / "cmdline" - cmdline_path.write_text(f"talos.environment=INSTALLER_META_BASE64={installer_meta}\n", encoding="utf-8") + extract_dir = temp_path / "extract" + extract_dir.mkdir() + output_tmp = temp_path / output_filename + + subprocess.run([xorriso, "-osirrox", "on", "-indev", str(storage_path), "-extract", "/", str(extract_dir)], check=True) + + isolinux_cfg = extract_dir / "isolinux" / "isolinux.cfg" + grub_cfg = extract_dir / "boot" / "grub" / "grub.cfg" + append = f" talos.environment=INSTALLER_META_BASE64={installer_meta}" + for cfg in (isolinux_cfg, grub_cfg): + if cfg.exists(): + text = cfg.read_text(encoding="utf-8") + text = text.replace("---", f"{append} ---", 1) + cfg.write_text(text, encoding="utf-8") + subprocess.run([ - "docker", "run", "--rm", - "-v", f"{script_path}:/input/build.sh:ro", - "-v", f"{cmdline_path}:/input/cmdline:ro", - "-v", f"{Path(storage_path).resolve()}:/input/source.iso:ro", - "-v", f"{temp_path}:/work", - "-v", f"{Path(output_path).resolve().parent}:/output", - "alpine:3.20", "sh", "/input/build.sh", + xorriso, + "-as", "mkisofs", + "-o", str(output_tmp), + "-isohybrid-mbr", str(extract_dir / "isolinux" / "isohdpfx.bin"), + "-c", "isolinux/boot.cat", + "-b", "isolinux/isolinux.bin", + "-no-emul-boot", + "-boot-load-size", "4", + "-boot-info-table", + "-eltorito-alt-boot", + "-e", "EFI/BOOT/BOOTX64.EFI", + "-no-emul-boot", + "-isohybrid-gpt-basdat", + "-V", "TALOS", + str(extract_dir), ], check=True) + shutil.copyfile(output_tmp, output_path) + def main(): parser = argparse.ArgumentParser()