fix: build talos iso without docker

This commit is contained in:
eding 2026-07-19 22:59:22 +02:00
parent d61225a9d3
commit aecbf392bc

View file

@ -4,10 +4,10 @@ import gzip
import json import json
import os import os
import shutil import shutil
import ssl
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
import ssl
import time import time
import urllib.error import urllib.error
import urllib.parse 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") 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): 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}" iso_dir = detect_proxmox_iso_dir(storage)
output_path = f"/var/lib/vz/template/iso/{output_filename}" storage_path = iso_dir / source_filename
output_path = iso_dir / output_filename
network_config = Path(network_config_path).read_text(encoding="utf-8").strip() 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") installer_meta = base64.b64encode(gzip.compress(f"0xa={network_config}".encode("utf-8"), compresslevel=9)).decode("ascii")
if shutil.which("docker") is None: xorriso = shutil.which("xorriso")
raise RuntimeError("docker is required to build Talos ISO with embedded META") if xorriso is None:
raise RuntimeError("xorriso is required to build Talos ISO with embedded META")
with tempfile.TemporaryDirectory() as temp_dir: with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir) temp_path = Path(temp_dir)
script_path = temp_path / "build.sh" extract_dir = temp_path / "extract"
script_path.write_text( extract_dir.mkdir()
"#!/bin/sh\n" output_tmp = temp_path / output_filename
"set -eu\n"
"apk add --no-cache xorriso >/dev/null\n" subprocess.run([xorriso, "-osirrox", "on", "-indev", str(storage_path), "-extract", "/", str(extract_dir)], check=True)
"cp /input/source.iso /work/source.iso\n"
"xorriso -indev /work/source.iso -outdev /output/result.iso -boot_image any replay \\\n" isolinux_cfg = extract_dir / "isolinux" / "isolinux.cfg"
f" -append_partition 2 0x0 /dev/null \\\n" grub_cfg = extract_dir / "boot" / "grub" / "grub.cfg"
f" -boot_image any keep \\\n" append = f" talos.environment=INSTALLER_META_BASE64={installer_meta}"
f" -map /input/cmdline /cmdline\n", for cfg in (isolinux_cfg, grub_cfg):
encoding="utf-8", if cfg.exists():
) text = cfg.read_text(encoding="utf-8")
cmdline_path = temp_path / "cmdline" text = text.replace("---", f"{append} ---", 1)
cmdline_path.write_text(f"talos.environment=INSTALLER_META_BASE64={installer_meta}\n", encoding="utf-8") cfg.write_text(text, encoding="utf-8")
subprocess.run([ subprocess.run([
"docker", "run", "--rm", xorriso,
"-v", f"{script_path}:/input/build.sh:ro", "-as", "mkisofs",
"-v", f"{cmdline_path}:/input/cmdline:ro", "-o", str(output_tmp),
"-v", f"{Path(storage_path).resolve()}:/input/source.iso:ro", "-isohybrid-mbr", str(extract_dir / "isolinux" / "isohdpfx.bin"),
"-v", f"{temp_path}:/work", "-c", "isolinux/boot.cat",
"-v", f"{Path(output_path).resolve().parent}:/output", "-b", "isolinux/isolinux.bin",
"alpine:3.20", "sh", "/input/build.sh", "-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) ], check=True)
shutil.copyfile(output_tmp, output_path)
def main(): def main():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()