nixos/systemd-boot-builder: warn instead of aborting when an old gen's secrets fail

After removing or renaming a file in boot.initrd.secrets, older
generations' append scripts start failing. Aborting on that blocks
deploying the new configuration, so only treat a failure as fatal when
it belongs to the configuration being switched to.
This commit is contained in:
r-vdp
2026-05-04 12:17:24 +02:00
parent 6eba7d60f3
commit 146acf965f
@@ -140,23 +140,30 @@ class SystemIdentifier(NamedTuple):
@dataclass
class BootFile:
system_identifier: SystemIdentifier
current: bool
path: Path
writer: WriteBootFile
@staticmethod
def from_source(system_identifier: SystemIdentifier, source: Path) -> "BootFile":
def from_source(
system_identifier: SystemIdentifier, current: bool, source: Path
) -> "BootFile":
return BootFile(
system_identifier=system_identifier,
current=current,
path=boot_path(source),
writer=CopyWriter(source=source),
)
@staticmethod
def from_initrd(
system_identifier: SystemIdentifier, source: Path, initrd_secrets: Path | None
system_identifier: SystemIdentifier,
current: bool,
source: Path,
initrd_secrets: Path | None,
) -> "BootFile":
if initrd_secrets is None:
return BootFile.from_source(system_identifier, source)
return BootFile.from_source(system_identifier, current, source)
else:
# We're trying to calculate a canonical path unique to
# this initrd and secret-appender. The boot_path is the
@@ -173,6 +180,7 @@ class BootFile:
combined_hash = hashlib.sha256(combined.encode("utf-8")).hexdigest()
return BootFile(
system_identifier=system_identifier,
current=current,
path=NIXOS_DIR / f"{combined_hash}-initrd.efi",
writer=InitrdWithSecretsWriter(
source=source, initrd_secrets=initrd_secrets
@@ -181,7 +189,7 @@ class BootFile:
@staticmethod
def from_entry(
system_identifier: SystemIdentifier, contents: bytes
system_identifier: SystemIdentifier, current: bool, contents: bytes
) -> tuple["BootFile", str]:
contents_hash = hashlib.sha256(contents).hexdigest()
path_prefix = f"nixos-{contents_hash}"
@@ -199,6 +207,7 @@ class BootFile:
return (
BootFile(
system_identifier=system_identifier,
current=current,
path=path,
writer=ContentsWriter(contents=contents),
),
@@ -336,19 +345,23 @@ def boot_file(
specialisation: str | None,
machine_id: str | None,
bootspec: BootSpec,
current: bool,
) -> tuple[BootFileList, str]:
system_identifier = SystemIdentifier(profile, generation, specialisation)
if specialisation:
bootspec = bootspec.specialisations[specialisation]
kernel = BootFile.from_source(system_identifier, bootspec.kernel)
kernel = BootFile.from_source(system_identifier, current, bootspec.kernel)
initrd = BootFile.from_initrd(
system_identifier,
current,
bootspec.initrd,
Path(bootspec.initrdSecrets) if bootspec.initrdSecrets is not None else None,
)
devicetree = None
if bootspec.devicetree is not None:
devicetree = BootFile.from_source(system_identifier, bootspec.devicetree)
devicetree = BootFile.from_source(
system_identifier, current, bootspec.devicetree
)
kernel_params = " ".join([f"init={bootspec.init}"] + bootspec.kernelParams)
build_time = int(system_dir(profile, generation, specialisation).stat().st_ctime)
@@ -371,7 +384,9 @@ def boot_file(
f"sort-key {bootspec.sortKey}",
]
contents = "\n".join(filter(None, boot_entry))
entry, bootctl_id = BootFile.from_entry(system_identifier, contents.encode("utf-8"))
entry, bootctl_id = BootFile.from_entry(
system_identifier, current, contents.encode("utf-8")
)
return (list(filter(None, [kernel, initrd, devicetree, entry])), bootctl_id)
@@ -520,7 +535,9 @@ def install_bootloader(args: argparse.Namespace) -> None:
for gen in gens:
bootspec = get_bootspec(gen.profile, gen.generation)
is_default = Path(bootspec.init).parent == default_config
new_boot_files, new_bootctl_id = boot_file(*gen, machine_id, bootspec)
new_boot_files, new_bootctl_id = boot_file(
*gen, machine_id, bootspec, current=is_default
)
boot_files.extend(new_boot_files)
if is_default:
default_entry_id = new_bootctl_id
@@ -532,6 +549,7 @@ def install_bootloader(args: argparse.Namespace) -> None:
specialisation_name,
machine_id,
bootspec,
current=is_default,
)
boot_files.extend(new_boot_files)
if is_default:
@@ -599,7 +617,22 @@ def garbage_collect(gc_roots: BootFileList) -> None:
def write_boot_files(boot_files: BootFileList) -> None:
for boot_file in boot_files:
boot_path = BOOT_MOUNT_POINT / boot_file.path
boot_file.writer.write_boot_file(boot_path)
try:
boot_file.writer.write_boot_file(boot_path)
except subprocess.CalledProcessError:
if boot_file.current:
print("failed to create initrd secrets!", file=sys.stderr)
sys.exit(1)
print(
"warning: failed to create initrd secrets for generation "
f"{boot_file.system_identifier.generation}, an older generation",
file=sys.stderr,
)
print(
"note: this is normal after having removed "
"or renamed a file in `boot.initrd.secrets`",
file=sys.stderr,
)
def main() -> None: