nixos-init: skip the etc overlay for a non-NixOS init=
find-etc verified the init= was inside a NixOS toplevel and bailed otherwise, failing initrd-find-etc.service. The etc-overlay mounts require it, so for a non-NixOS init= (e.g. init=/bin/sh) the initrd dropped to emergency mode before initrd-init could switch-root into the target. Make find-etc skip silently for a non-NixOS init=, leaving the /etc-basedir and /etc-metadata-image symlinks uncreated, and gate the etc-metadata mount, the /sysroot/etc overlay and the rw-etc service on those symlinks with ConditionPathExists so they skip instead of fail. initrd-init then switch-roots into the non-NixOS init directly. The NixOS path is unchanged: the symlinks exist, so the conditions hold. Extend the systemd-initrd-non-nixos test with a second node enabling system.nixos-init.enable, so both the bash initrd-nixos-activation path and the nixos-init path are covered. Assisted-by: Claude:claude-opus-4-8
This commit is contained in:
@@ -71,6 +71,10 @@
|
||||
RequiresMountsFor = [
|
||||
"/sysroot/nix/store"
|
||||
];
|
||||
# find-etc only creates this symlink for a NixOS init. For a
|
||||
# non-NixOS init= (e.g. init=/bin/sh) it is absent, so skip the
|
||||
# mount instead of failing the whole initrd.
|
||||
ConditionPathExists = "/etc-metadata-image";
|
||||
};
|
||||
requires = [
|
||||
config.boot.initrd.systemd.services.initrd-find-etc.name
|
||||
@@ -123,6 +127,8 @@
|
||||
"/run/nixos-etc-metadata"
|
||||
];
|
||||
DefaultDependencies = false;
|
||||
# Skip for a non-NixOS init=, see the metadata mount above.
|
||||
ConditionPathExists = "/etc-basedir";
|
||||
};
|
||||
}
|
||||
];
|
||||
@@ -140,6 +146,8 @@
|
||||
# before the overlay is mounted.
|
||||
"/run/nixos-etc-metadata"
|
||||
];
|
||||
# Skip for a non-NixOS init=, see the metadata mount above.
|
||||
ConditionPathExists = "/etc-metadata-image";
|
||||
};
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
|
||||
@@ -10,11 +10,8 @@ let
|
||||
echo "${marker}" > /dev/console
|
||||
exec ${pkgs.coreutils}/bin/sleep infinity
|
||||
'';
|
||||
in
|
||||
{
|
||||
name = "systemd-initrd-non-nixos";
|
||||
|
||||
nodes.machine = {
|
||||
common = {
|
||||
boot.initrd.systemd.enable = true;
|
||||
|
||||
virtualisation = {
|
||||
@@ -35,6 +32,20 @@ in
|
||||
argument = "ID=test-non-nixos";
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
name = "systemd-initrd-non-nixos";
|
||||
|
||||
nodes = {
|
||||
bashActivation = common;
|
||||
|
||||
nixosInit = {
|
||||
imports = [ common ];
|
||||
system.nixos-init.enable = true;
|
||||
system.etc.overlay.enable = true;
|
||||
services.userborn.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
import os
|
||||
@@ -43,11 +54,15 @@ in
|
||||
# the default one, so this boots our non-NixOS init.
|
||||
os.environ["QEMU_KERNEL_PARAMS"] = "init=${lib.getExe nonNixosInit}"
|
||||
|
||||
machine.start()
|
||||
start_all()
|
||||
|
||||
# If activation does not skip the non-NixOS init, switch-root is blocked and
|
||||
# the machine drops to emergency mode: the marker never appears and this
|
||||
# If a code path does not skip the non-NixOS init, switch-root is blocked and
|
||||
# the machine drops to emergency mode: the marker never appears and the wait
|
||||
# times out.
|
||||
machine.wait_for_console_text("${marker}", timeout=300)
|
||||
with subtest("bash initrd-nixos-activation skips a non-NixOS init"):
|
||||
bashActivation.wait_for_console_text("${marker}", timeout=300)
|
||||
|
||||
with subtest("nixos-init switches to a non-NixOS init directly"):
|
||||
nixosInit.wait_for_console_text("${marker}", timeout=300)
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::{os::unix, path::Path};
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::{SYSROOT_PATH, find_toplevel_in_prefix, resolve_in_prefix};
|
||||
use crate::{SYSROOT_PATH, find_init_in_prefix, resolve_in_prefix, verify_init_is_nixos};
|
||||
|
||||
/// Entrypoint for the `find-etc` binary.
|
||||
///
|
||||
@@ -12,7 +12,20 @@ use crate::{SYSROOT_PATH, find_toplevel_in_prefix, resolve_in_prefix};
|
||||
/// This avoids needing a reference to the toplevel embedded in the initrd and thus reduces the
|
||||
/// need to re-build it.
|
||||
pub fn find_etc() -> Result<()> {
|
||||
let toplevel = find_toplevel_in_prefix(SYSROOT_PATH)?;
|
||||
let init_in_sysroot =
|
||||
find_init_in_prefix(SYSROOT_PATH).context("Failed to find init in sysroot")?;
|
||||
|
||||
// A non-NixOS init= (e.g. init=/bin/sh) has no etc metadata image. Skip
|
||||
// without creating the symlinks: the etc-overlay mounts are gated on them
|
||||
// and so skip too, and initrd-init switches root to the init directly.
|
||||
let Ok(toplevel) = verify_init_is_nixos(SYSROOT_PATH, &init_in_sysroot) else {
|
||||
log::info!(
|
||||
"{} is not a NixOS system - not setting up the etc overlay.",
|
||||
init_in_sysroot.display()
|
||||
);
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let config = Config::from_toplevel(&toplevel, SYSROOT_PATH)?;
|
||||
|
||||
let basedir = config
|
||||
|
||||
@@ -27,16 +27,6 @@ pub use crate::{
|
||||
|
||||
pub const SYSROOT_PATH: &str = "/sysroot";
|
||||
|
||||
/// Find the path to the toplevel closure of the system in a prefix.
|
||||
///
|
||||
/// Uses the `init=` parameter on the kernel command-line.
|
||||
///
|
||||
/// Returns the relative path of the init to the prefix, e.g. without the `/sysroot` prefix.
|
||||
pub fn find_toplevel_in_prefix(prefix: &str) -> Result<PathBuf> {
|
||||
let init_in_sysroot = find_init_in_prefix(prefix)?;
|
||||
verify_init_is_nixos(prefix, init_in_sysroot)
|
||||
}
|
||||
|
||||
/// Verify that an init path is inside a `NixOS` toplevel directory.
|
||||
///
|
||||
/// If the path is verified, returns the path to the toplevel.
|
||||
|
||||
Reference in New Issue
Block a user