mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-06-12 20:55:31 +03:00
Merge pull request #237040 from roberth/flexible-activation
nixos/system: Support pre-activated images
This commit is contained in:
commit
3fd4ac8e82
6 changed files with 183 additions and 74 deletions
|
@ -1340,6 +1340,7 @@
|
||||||
./services/x11/xbanish.nix
|
./services/x11/xbanish.nix
|
||||||
./services/x11/xfs.nix
|
./services/x11/xfs.nix
|
||||||
./services/x11/xserver.nix
|
./services/x11/xserver.nix
|
||||||
|
./system/activation/activatable-system.nix
|
||||||
./system/activation/activation-script.nix
|
./system/activation/activation-script.nix
|
||||||
./system/activation/specialisation.nix
|
./system/activation/specialisation.nix
|
||||||
./system/activation/bootspec.nix
|
./system/activation/bootspec.nix
|
||||||
|
|
92
nixos/modules/system/activation/activatable-system.nix
Normal file
92
nixos/modules/system/activation/activatable-system.nix
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (lib)
|
||||||
|
mkOption
|
||||||
|
optionalString
|
||||||
|
types
|
||||||
|
;
|
||||||
|
|
||||||
|
perlWrapped = pkgs.perl.withPackages (p: with p; [ ConfigIniFiles FileSlurp ]);
|
||||||
|
|
||||||
|
systemBuilderArgs = {
|
||||||
|
activationScript = config.system.activationScripts.script;
|
||||||
|
dryActivationScript = config.system.dryActivationScript;
|
||||||
|
};
|
||||||
|
|
||||||
|
systemBuilderCommands = ''
|
||||||
|
echo "$activationScript" > $out/activate
|
||||||
|
echo "$dryActivationScript" > $out/dry-activate
|
||||||
|
substituteInPlace $out/activate --subst-var-by out ''${!toplevelVar}
|
||||||
|
substituteInPlace $out/dry-activate --subst-var-by out ''${!toplevelVar}
|
||||||
|
chmod u+x $out/activate $out/dry-activate
|
||||||
|
unset activationScript dryActivationScript
|
||||||
|
|
||||||
|
mkdir $out/bin
|
||||||
|
substitute ${./switch-to-configuration.pl} $out/bin/switch-to-configuration \
|
||||||
|
--subst-var out \
|
||||||
|
--subst-var-by toplevel ''${!toplevelVar} \
|
||||||
|
--subst-var-by coreutils "${pkgs.coreutils}" \
|
||||||
|
--subst-var-by distroId ${lib.escapeShellArg config.system.nixos.distroId} \
|
||||||
|
--subst-var-by installBootLoader ${lib.escapeShellArg config.system.build.installBootLoader} \
|
||||||
|
--subst-var-by localeArchive "${config.i18n.glibcLocales}/lib/locale/locale-archive" \
|
||||||
|
--subst-var-by perl "${perlWrapped}" \
|
||||||
|
--subst-var-by shell "${pkgs.bash}/bin/sh" \
|
||||||
|
--subst-var-by su "${pkgs.shadow.su}/bin/su" \
|
||||||
|
--subst-var-by systemd "${config.systemd.package}" \
|
||||||
|
--subst-var-by utillinux "${pkgs.util-linux}" \
|
||||||
|
;
|
||||||
|
|
||||||
|
chmod +x $out/bin/switch-to-configuration
|
||||||
|
${optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) ''
|
||||||
|
if ! output=$(${perlWrapped}/bin/perl -c $out/bin/switch-to-configuration 2>&1); then
|
||||||
|
echo "switch-to-configuration syntax is not valid:"
|
||||||
|
echo "$output"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
''}
|
||||||
|
'';
|
||||||
|
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
system.activatable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Whether to add the activation script to the system profile.
|
||||||
|
|
||||||
|
The default, to have the script available all the time, is what we normally
|
||||||
|
do, but for image based systems, this may not be needed or not be desirable.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
system.build.separateActivationScript = mkOption {
|
||||||
|
type = types.package;
|
||||||
|
description = ''
|
||||||
|
A separate activation script package that's not part of the system profile.
|
||||||
|
|
||||||
|
This is useful for configurations where `system.activatable` is `false`.
|
||||||
|
Otherwise, you can just use `system.build.toplevel`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = {
|
||||||
|
system.systemBuilderCommands = lib.mkIf config.system.activatable systemBuilderCommands;
|
||||||
|
system.systemBuilderArgs = lib.mkIf config.system.activatable
|
||||||
|
(systemBuilderArgs // {
|
||||||
|
toplevelVar = "out";
|
||||||
|
});
|
||||||
|
|
||||||
|
system.build.separateActivationScript =
|
||||||
|
pkgs.runCommand
|
||||||
|
"separate-activation-script"
|
||||||
|
(systemBuilderArgs // {
|
||||||
|
toplevelVar = "toplevel";
|
||||||
|
toplevel = config.system.build.toplevel;
|
||||||
|
})
|
||||||
|
''
|
||||||
|
mkdir $out
|
||||||
|
${systemBuilderCommands}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
|
@ -204,6 +204,27 @@ in
|
||||||
`/usr/bin/env`.
|
`/usr/bin/env`.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
system.build.installBootLoader = mkOption {
|
||||||
|
internal = true;
|
||||||
|
# "; true" => make the `$out` argument from switch-to-configuration.pl
|
||||||
|
# go to `true` instead of `echo`, hiding the useless path
|
||||||
|
# from the log.
|
||||||
|
default = "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
|
||||||
|
description = lib.mdDoc ''
|
||||||
|
A program that writes a bootloader installation script to the path passed in the first command line argument.
|
||||||
|
|
||||||
|
See `nixos/modules/system/activation/switch-to-configuration.pl`.
|
||||||
|
'';
|
||||||
|
type = types.unique {
|
||||||
|
message = ''
|
||||||
|
Only one bootloader can be enabled at a time. This requirement has not
|
||||||
|
been checked until NixOS 22.05. Earlier versions defaulted to the last
|
||||||
|
definition. Change your configuration to enable only one bootloader.
|
||||||
|
'';
|
||||||
|
} (types.either types.str types.package);
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -31,8 +31,10 @@ use Cwd qw(abs_path);
|
||||||
## no critic(ValuesAndExpressions::ProhibitNoisyQuotes, ValuesAndExpressions::ProhibitMagicNumbers, ValuesAndExpressions::ProhibitEmptyQuotes, ValuesAndExpressions::ProhibitInterpolationOfLiterals)
|
## no critic(ValuesAndExpressions::ProhibitNoisyQuotes, ValuesAndExpressions::ProhibitMagicNumbers, ValuesAndExpressions::ProhibitEmptyQuotes, ValuesAndExpressions::ProhibitInterpolationOfLiterals)
|
||||||
## no critic(RegularExpressions::ProhibitEscapedMetacharacters)
|
## no critic(RegularExpressions::ProhibitEscapedMetacharacters)
|
||||||
|
|
||||||
# System closure path to switch to
|
# Location of activation scripts
|
||||||
my $out = "@out@";
|
my $out = "@out@";
|
||||||
|
# System closure path to switch to
|
||||||
|
my $toplevel = "@toplevel@";
|
||||||
# Path to the directory containing systemd tools of the old system
|
# Path to the directory containing systemd tools of the old system
|
||||||
my $cur_systemd = abs_path("/run/current-system/sw/bin");
|
my $cur_systemd = abs_path("/run/current-system/sw/bin");
|
||||||
# Path to the systemd store path of the new system
|
# Path to the systemd store path of the new system
|
||||||
|
@ -96,7 +98,7 @@ if ($action eq "switch" || $action eq "boot") {
|
||||||
chomp(my $install_boot_loader = <<'EOFBOOTLOADER');
|
chomp(my $install_boot_loader = <<'EOFBOOTLOADER');
|
||||||
@installBootLoader@
|
@installBootLoader@
|
||||||
EOFBOOTLOADER
|
EOFBOOTLOADER
|
||||||
system("$install_boot_loader $out") == 0 or exit 1;
|
system("$install_boot_loader $toplevel") == 0 or exit 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Just in case the new configuration hangs the system, do a sync now.
|
# Just in case the new configuration hangs the system, do a sync now.
|
||||||
|
@ -110,7 +112,7 @@ if ($action eq "boot") {
|
||||||
|
|
||||||
# Check if we can activate the new configuration.
|
# Check if we can activate the new configuration.
|
||||||
my $cur_init_interface_version = read_file("/run/current-system/init-interface-version", err_mode => "quiet") // "";
|
my $cur_init_interface_version = read_file("/run/current-system/init-interface-version", err_mode => "quiet") // "";
|
||||||
my $new_init_interface_version = read_file("$out/init-interface-version");
|
my $new_init_interface_version = read_file("$toplevel/init-interface-version");
|
||||||
|
|
||||||
if ($new_init_interface_version ne $cur_init_interface_version) {
|
if ($new_init_interface_version ne $cur_init_interface_version) {
|
||||||
print STDERR <<'EOF';
|
print STDERR <<'EOF';
|
||||||
|
@ -477,7 +479,7 @@ sub handle_modified_unit { ## no critic(Subroutines::ProhibitManyArgs, Subroutin
|
||||||
$units_to_stop->{$socket} = 1;
|
$units_to_stop->{$socket} = 1;
|
||||||
# Only restart sockets that actually
|
# Only restart sockets that actually
|
||||||
# exist in new configuration:
|
# exist in new configuration:
|
||||||
if (-e "$out/etc/systemd/system/$socket") {
|
if (-e "$toplevel/etc/systemd/system/$socket") {
|
||||||
$units_to_start->{$socket} = 1;
|
$units_to_start->{$socket} = 1;
|
||||||
if ($units_to_start eq $units_to_restart) {
|
if ($units_to_start eq $units_to_restart) {
|
||||||
record_unit($restart_list_file, $socket);
|
record_unit($restart_list_file, $socket);
|
||||||
|
@ -539,13 +541,13 @@ while (my ($unit, $state) = each(%{$active_cur})) {
|
||||||
my $base_unit = $unit;
|
my $base_unit = $unit;
|
||||||
|
|
||||||
my $cur_unit_file = "/etc/systemd/system/$base_unit";
|
my $cur_unit_file = "/etc/systemd/system/$base_unit";
|
||||||
my $new_unit_file = "$out/etc/systemd/system/$base_unit";
|
my $new_unit_file = "$toplevel/etc/systemd/system/$base_unit";
|
||||||
|
|
||||||
# Detect template instances.
|
# Detect template instances.
|
||||||
if (!-e $cur_unit_file && !-e $new_unit_file && $unit =~ /^(.*)@[^\.]*\.(.*)$/msx) {
|
if (!-e $cur_unit_file && !-e $new_unit_file && $unit =~ /^(.*)@[^\.]*\.(.*)$/msx) {
|
||||||
$base_unit = "$1\@.$2";
|
$base_unit = "$1\@.$2";
|
||||||
$cur_unit_file = "/etc/systemd/system/$base_unit";
|
$cur_unit_file = "/etc/systemd/system/$base_unit";
|
||||||
$new_unit_file = "$out/etc/systemd/system/$base_unit";
|
$new_unit_file = "$toplevel/etc/systemd/system/$base_unit";
|
||||||
}
|
}
|
||||||
|
|
||||||
my $base_name = $base_unit;
|
my $base_name = $base_unit;
|
||||||
|
@ -626,7 +628,7 @@ sub path_to_unit_name {
|
||||||
# we generated units for all mounts; then we could unify this with the
|
# we generated units for all mounts; then we could unify this with the
|
||||||
# unit checking code above.
|
# unit checking code above.
|
||||||
my ($cur_fss, $cur_swaps) = parse_fstab("/etc/fstab");
|
my ($cur_fss, $cur_swaps) = parse_fstab("/etc/fstab");
|
||||||
my ($new_fss, $new_swaps) = parse_fstab("$out/etc/fstab");
|
my ($new_fss, $new_swaps) = parse_fstab("$toplevel/etc/fstab");
|
||||||
foreach my $mount_point (keys(%{$cur_fss})) {
|
foreach my $mount_point (keys(%{$cur_fss})) {
|
||||||
my $cur = $cur_fss->{$mount_point};
|
my $cur = $cur_fss->{$mount_point};
|
||||||
my $new = $new_fss->{$mount_point};
|
my $new = $new_fss->{$mount_point};
|
||||||
|
@ -670,7 +672,7 @@ foreach my $device (keys(%{$cur_swaps})) {
|
||||||
my $cur_pid1_path = abs_path("/proc/1/exe") // "/unknown";
|
my $cur_pid1_path = abs_path("/proc/1/exe") // "/unknown";
|
||||||
my $cur_systemd_system_config = abs_path("/etc/systemd/system.conf") // "/unknown";
|
my $cur_systemd_system_config = abs_path("/etc/systemd/system.conf") // "/unknown";
|
||||||
my $new_pid1_path = abs_path("$new_systemd/lib/systemd/systemd") or die;
|
my $new_pid1_path = abs_path("$new_systemd/lib/systemd/systemd") or die;
|
||||||
my $new_systemd_system_config = abs_path("$out/etc/systemd/system.conf") // "/unknown";
|
my $new_systemd_system_config = abs_path("$toplevel/etc/systemd/system.conf") // "/unknown";
|
||||||
|
|
||||||
my $restart_systemd = $cur_pid1_path ne $new_pid1_path;
|
my $restart_systemd = $cur_pid1_path ne $new_pid1_path;
|
||||||
if ($cur_systemd_system_config ne $new_systemd_system_config) {
|
if ($cur_systemd_system_config ne $new_systemd_system_config) {
|
||||||
|
@ -709,12 +711,12 @@ if ($action eq "dry-activate") {
|
||||||
foreach (split(/\n/msx, read_file($dry_restart_by_activation_file, err_mode => "quiet") // "")) {
|
foreach (split(/\n/msx, read_file($dry_restart_by_activation_file, err_mode => "quiet") // "")) {
|
||||||
my $unit = $_;
|
my $unit = $_;
|
||||||
my $base_unit = $unit;
|
my $base_unit = $unit;
|
||||||
my $new_unit_file = "$out/etc/systemd/system/$base_unit";
|
my $new_unit_file = "$toplevel/etc/systemd/system/$base_unit";
|
||||||
|
|
||||||
# Detect template instances.
|
# Detect template instances.
|
||||||
if (!-e $new_unit_file && $unit =~ /^(.*)@[^\.]*\.(.*)$/msx) {
|
if (!-e $new_unit_file && $unit =~ /^(.*)@[^\.]*\.(.*)$/msx) {
|
||||||
$base_unit = "$1\@.$2";
|
$base_unit = "$1\@.$2";
|
||||||
$new_unit_file = "$out/etc/systemd/system/$base_unit";
|
$new_unit_file = "$toplevel/etc/systemd/system/$base_unit";
|
||||||
}
|
}
|
||||||
|
|
||||||
my $base_name = $base_unit;
|
my $base_name = $base_unit;
|
||||||
|
@ -757,7 +759,7 @@ if ($action eq "dry-activate") {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
syslog(LOG_NOTICE, "switching to system configuration $out");
|
syslog(LOG_NOTICE, "switching to system configuration $toplevel");
|
||||||
|
|
||||||
if (scalar(keys(%units_to_stop)) > 0) {
|
if (scalar(keys(%units_to_stop)) > 0) {
|
||||||
if (scalar(@units_to_stop_filtered)) {
|
if (scalar(@units_to_stop_filtered)) {
|
||||||
|
@ -781,12 +783,12 @@ system("$out/activate", "$out") == 0 or $res = 2;
|
||||||
foreach (split(/\n/msx, read_file($restart_by_activation_file, err_mode => "quiet") // "")) {
|
foreach (split(/\n/msx, read_file($restart_by_activation_file, err_mode => "quiet") // "")) {
|
||||||
my $unit = $_;
|
my $unit = $_;
|
||||||
my $base_unit = $unit;
|
my $base_unit = $unit;
|
||||||
my $new_unit_file = "$out/etc/systemd/system/$base_unit";
|
my $new_unit_file = "$toplevel/etc/systemd/system/$base_unit";
|
||||||
|
|
||||||
# Detect template instances.
|
# Detect template instances.
|
||||||
if (!-e $new_unit_file && $unit =~ /^(.*)@[^\.]*\.(.*)$/msx) {
|
if (!-e $new_unit_file && $unit =~ /^(.*)@[^\.]*\.(.*)$/msx) {
|
||||||
$base_unit = "$1\@.$2";
|
$base_unit = "$1\@.$2";
|
||||||
$new_unit_file = "$out/etc/systemd/system/$base_unit";
|
$new_unit_file = "$toplevel/etc/systemd/system/$base_unit";
|
||||||
}
|
}
|
||||||
|
|
||||||
my $base_name = $base_unit;
|
my $base_name = $base_unit;
|
||||||
|
@ -857,7 +859,7 @@ if (scalar(keys(%units_to_reload)) > 0) {
|
||||||
for my $unit (keys(%units_to_reload)) {
|
for my $unit (keys(%units_to_reload)) {
|
||||||
if (!unit_is_active($unit)) {
|
if (!unit_is_active($unit)) {
|
||||||
# Figure out if we need to start the unit
|
# Figure out if we need to start the unit
|
||||||
my %unit_info = parse_unit("$out/etc/systemd/system/$unit");
|
my %unit_info = parse_unit("$toplevel/etc/systemd/system/$unit");
|
||||||
if (!(parse_systemd_bool(\%unit_info, "Unit", "RefuseManualStart", 0) || parse_systemd_bool(\%unit_info, "Unit", "X-OnlyManualStart", 0))) {
|
if (!(parse_systemd_bool(\%unit_info, "Unit", "RefuseManualStart", 0) || parse_systemd_bool(\%unit_info, "Unit", "X-OnlyManualStart", 0))) {
|
||||||
$units_to_start{$unit} = 1;
|
$units_to_start{$unit} = 1;
|
||||||
record_unit($start_list_file, $unit);
|
record_unit($start_list_file, $unit);
|
||||||
|
@ -940,9 +942,9 @@ if (scalar(@failed) > 0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($res == 0) {
|
if ($res == 0) {
|
||||||
syslog(LOG_NOTICE, "finished switching to system configuration $out");
|
syslog(LOG_NOTICE, "finished switching to system configuration $toplevel");
|
||||||
} else {
|
} else {
|
||||||
syslog(LOG_ERR, "switching to system configuration $out failed (status $res)");
|
syslog(LOG_ERR, "switching to system configuration $toplevel failed (status $res)");
|
||||||
}
|
}
|
||||||
|
|
||||||
exit($res);
|
exit($res);
|
||||||
|
|
|
@ -36,13 +36,6 @@ let
|
||||||
ln -s ${config.hardware.firmware}/lib/firmware $out/firmware
|
ln -s ${config.hardware.firmware}/lib/firmware $out/firmware
|
||||||
''}
|
''}
|
||||||
|
|
||||||
echo "$activationScript" > $out/activate
|
|
||||||
echo "$dryActivationScript" > $out/dry-activate
|
|
||||||
substituteInPlace $out/activate --subst-var out
|
|
||||||
substituteInPlace $out/dry-activate --subst-var out
|
|
||||||
chmod u+x $out/activate $out/dry-activate
|
|
||||||
unset activationScript dryActivationScript
|
|
||||||
|
|
||||||
${if config.boot.initrd.systemd.enable then ''
|
${if config.boot.initrd.systemd.enable then ''
|
||||||
cp ${config.system.build.bootStage2} $out/prepare-root
|
cp ${config.system.build.bootStage2} $out/prepare-root
|
||||||
substituteInPlace $out/prepare-root --subst-var-by systemConfig $out
|
substituteInPlace $out/prepare-root --subst-var-by systemConfig $out
|
||||||
|
@ -63,19 +56,6 @@ let
|
||||||
echo -n "$nixosLabel" > $out/nixos-version
|
echo -n "$nixosLabel" > $out/nixos-version
|
||||||
echo -n "${config.boot.kernelPackages.stdenv.hostPlatform.system}" > $out/system
|
echo -n "${config.boot.kernelPackages.stdenv.hostPlatform.system}" > $out/system
|
||||||
|
|
||||||
mkdir $out/bin
|
|
||||||
export localeArchive="${config.i18n.glibcLocales}/lib/locale/locale-archive"
|
|
||||||
export distroId=${config.system.nixos.distroId};
|
|
||||||
substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration
|
|
||||||
chmod +x $out/bin/switch-to-configuration
|
|
||||||
${optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) ''
|
|
||||||
if ! output=$($perl/bin/perl -c $out/bin/switch-to-configuration 2>&1); then
|
|
||||||
echo "switch-to-configuration syntax is not valid:"
|
|
||||||
echo "$output"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
''}
|
|
||||||
|
|
||||||
${config.system.systemBuilderCommands}
|
${config.system.systemBuilderCommands}
|
||||||
|
|
||||||
cp "$extraDependenciesPath" "$out/extra-dependencies"
|
cp "$extraDependenciesPath" "$out/extra-dependencies"
|
||||||
|
@ -93,7 +73,7 @@ let
|
||||||
# symlinks to the various parts of the built configuration (the
|
# symlinks to the various parts of the built configuration (the
|
||||||
# kernel, systemd units, init scripts, etc.) as well as a script
|
# kernel, systemd units, init scripts, etc.) as well as a script
|
||||||
# `switch-to-configuration' that activates the configuration and
|
# `switch-to-configuration' that activates the configuration and
|
||||||
# makes it bootable.
|
# makes it bootable. See `activatable-system.nix`.
|
||||||
baseSystem = pkgs.stdenvNoCC.mkDerivation ({
|
baseSystem = pkgs.stdenvNoCC.mkDerivation ({
|
||||||
name = "nixos-system-${config.system.name}-${config.system.nixos.label}";
|
name = "nixos-system-${config.system.name}-${config.system.nixos.label}";
|
||||||
preferLocalBuild = true;
|
preferLocalBuild = true;
|
||||||
|
@ -101,22 +81,12 @@ let
|
||||||
passAsFile = [ "extraDependencies" ];
|
passAsFile = [ "extraDependencies" ];
|
||||||
buildCommand = systemBuilder;
|
buildCommand = systemBuilder;
|
||||||
|
|
||||||
inherit (pkgs) coreutils;
|
|
||||||
systemd = config.systemd.package;
|
systemd = config.systemd.package;
|
||||||
shell = "${pkgs.bash}/bin/sh";
|
|
||||||
su = "${pkgs.shadow.su}/bin/su";
|
|
||||||
utillinux = pkgs.util-linux;
|
|
||||||
|
|
||||||
kernelParams = config.boot.kernelParams;
|
kernelParams = config.boot.kernelParams;
|
||||||
installBootLoader = config.system.build.installBootLoader;
|
|
||||||
activationScript = config.system.activationScripts.script;
|
|
||||||
dryActivationScript = config.system.dryActivationScript;
|
|
||||||
nixosLabel = config.system.nixos.label;
|
nixosLabel = config.system.nixos.label;
|
||||||
|
|
||||||
inherit (config.system) extraDependencies;
|
inherit (config.system) extraDependencies;
|
||||||
|
|
||||||
# Needed by switch-to-configuration.
|
|
||||||
perl = pkgs.perl.withPackages (p: with p; [ ConfigIniFiles FileSlurp ]);
|
|
||||||
} // config.system.systemBuilderArgs);
|
} // config.system.systemBuilderArgs);
|
||||||
|
|
||||||
# Handle assertions and warnings
|
# Handle assertions and warnings
|
||||||
|
@ -178,26 +148,6 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
system.build = {
|
system.build = {
|
||||||
installBootLoader = mkOption {
|
|
||||||
internal = true;
|
|
||||||
# "; true" => make the `$out` argument from switch-to-configuration.pl
|
|
||||||
# go to `true` instead of `echo`, hiding the useless path
|
|
||||||
# from the log.
|
|
||||||
default = "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
|
|
||||||
description = lib.mdDoc ''
|
|
||||||
A program that writes a bootloader installation script to the path passed in the first command line argument.
|
|
||||||
|
|
||||||
See `nixos/modules/system/activation/switch-to-configuration.pl`.
|
|
||||||
'';
|
|
||||||
type = types.unique {
|
|
||||||
message = ''
|
|
||||||
Only one bootloader can be enabled at a time. This requirement has not
|
|
||||||
been checked until NixOS 22.05. Earlier versions defaulted to the last
|
|
||||||
definition. Change your configuration to enable only one bootloader.
|
|
||||||
'';
|
|
||||||
} (types.either types.str types.package);
|
|
||||||
};
|
|
||||||
|
|
||||||
toplevel = mkOption {
|
toplevel = mkOption {
|
||||||
type = types.package;
|
type = types.package;
|
||||||
readOnly = true;
|
readOnly = true;
|
||||||
|
@ -380,6 +330,16 @@ in
|
||||||
'';
|
'';
|
||||||
|
|
||||||
system.systemBuilderArgs = {
|
system.systemBuilderArgs = {
|
||||||
|
|
||||||
|
# Legacy environment variables. These were used by the activation script,
|
||||||
|
# but some other script might still depend on them, although unlikely.
|
||||||
|
installBootLoader = config.system.build.installBootLoader;
|
||||||
|
localeArchive = "${config.i18n.glibcLocales}/lib/locale/locale-archive";
|
||||||
|
distroId = config.system.nixos.distroId;
|
||||||
|
perl = pkgs.perl.withPackages (p: with p; [ ConfigIniFiles FileSlurp ]);
|
||||||
|
# End if legacy environment variables
|
||||||
|
|
||||||
|
|
||||||
# Not actually used in the builder. `passedChecks` is just here to create
|
# Not actually used in the builder. `passedChecks` is just here to create
|
||||||
# the build dependencies. Checks are similar to build dependencies in the
|
# the build dependencies. Checks are similar to build dependencies in the
|
||||||
# sense that if they fail, the system build fails. However, checks do not
|
# sense that if they fail, the system build fails. However, checks do not
|
||||||
|
|
|
@ -70,6 +70,19 @@ in {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
simpleServiceSeparateActivationScript.configuration = {
|
||||||
|
system.activatable = false;
|
||||||
|
systemd.services.test = {
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
ExecStart = "${pkgs.coreutils}/bin/true";
|
||||||
|
ExecReload = "${pkgs.coreutils}/bin/true";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
simpleServiceDifferentDescription.configuration = {
|
simpleServiceDifferentDescription.configuration = {
|
||||||
imports = [ simpleService.configuration ];
|
imports = [ simpleService.configuration ];
|
||||||
systemd.services.test.description = "Test unit";
|
systemd.services.test.description = "Test unit";
|
||||||
|
@ -482,9 +495,9 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
testScript = { nodes, ... }: let
|
testScript = { nodes, ... }: let
|
||||||
originalSystem = nodes.machine.config.system.build.toplevel;
|
originalSystem = nodes.machine.system.build.toplevel;
|
||||||
otherSystem = nodes.other.config.system.build.toplevel;
|
otherSystem = nodes.other.system.build.toplevel;
|
||||||
machine = nodes.machine.config.system.build.toplevel;
|
machine = nodes.machine.system.build.toplevel;
|
||||||
|
|
||||||
# Ensures failures pass through using pipefail, otherwise failing to
|
# Ensures failures pass through using pipefail, otherwise failing to
|
||||||
# switch-to-configuration is hidden by the success of `tee`.
|
# switch-to-configuration is hidden by the success of `tee`.
|
||||||
|
@ -497,11 +510,15 @@ in {
|
||||||
in /* python */ ''
|
in /* python */ ''
|
||||||
def switch_to_specialisation(system, name, action="test", fail=False):
|
def switch_to_specialisation(system, name, action="test", fail=False):
|
||||||
if name == "":
|
if name == "":
|
||||||
stc = f"{system}/bin/switch-to-configuration"
|
switcher = f"{system}/bin/switch-to-configuration"
|
||||||
else:
|
else:
|
||||||
stc = f"{system}/specialisation/{name}/bin/switch-to-configuration"
|
switcher = f"{system}/specialisation/{name}/bin/switch-to-configuration"
|
||||||
out = machine.fail(f"{stc} {action} 2>&1") if fail \
|
return run_switch(switcher, action, fail)
|
||||||
else machine.succeed(f"{stc} {action} 2>&1")
|
|
||||||
|
# like above but stc = switcher
|
||||||
|
def run_switch(switcher, action="test", fail=False):
|
||||||
|
out = machine.fail(f"{switcher} {action} 2>&1") if fail \
|
||||||
|
else machine.succeed(f"{switcher} {action} 2>&1")
|
||||||
assert_lacks(out, "switch-to-configuration line") # Perl warnings
|
assert_lacks(out, "switch-to-configuration line") # Perl warnings
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
@ -639,6 +656,22 @@ in {
|
||||||
assert_lacks(out, "the following new units were started:")
|
assert_lacks(out, "the following new units were started:")
|
||||||
assert_contains(out, "would start the following units: test.service\n")
|
assert_contains(out, "would start the following units: test.service\n")
|
||||||
|
|
||||||
|
out = switch_to_specialisation("${machine}", "", action="test")
|
||||||
|
|
||||||
|
# Ensure the service can be started when the activation script isn't in toplevel
|
||||||
|
# This is a lot like "Start a simple service", except activation-only deps could be gc-ed
|
||||||
|
out = run_switch("${nodes.machine.specialisation.simpleServiceSeparateActivationScript.configuration.system.build.separateActivationScript}/bin/switch-to-configuration");
|
||||||
|
assert_lacks(out, "installing dummy bootloader") # test does not install a bootloader
|
||||||
|
assert_lacks(out, "stopping the following units:")
|
||||||
|
assert_lacks(out, "NOT restarting the following changed units:")
|
||||||
|
assert_contains(out, "reloading the following units: dbus.service\n") # huh
|
||||||
|
assert_lacks(out, "\nrestarting the following units:")
|
||||||
|
assert_lacks(out, "\nstarting the following units:")
|
||||||
|
assert_contains(out, "the following new units were started: test.service\n")
|
||||||
|
machine.succeed("! test -e /run/current-system/activate")
|
||||||
|
machine.succeed("! test -e /run/current-system/dry-activate")
|
||||||
|
machine.succeed("! test -e /run/current-system/bin/switch-to-configuration")
|
||||||
|
|
||||||
# Ensure \ works in unit names
|
# Ensure \ works in unit names
|
||||||
out = switch_to_specialisation("${machine}", "unitWithBackslash")
|
out = switch_to_specialisation("${machine}", "unitWithBackslash")
|
||||||
assert_contains(out, "stopping the following units: test.service\n")
|
assert_contains(out, "stopping the following units: test.service\n")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue