modules/tmproot: Make persistence optional
This commit is contained in:
parent
7dec8bb56b
commit
e306da7ce6
@ -1,7 +1,7 @@
|
|||||||
{ lib, extendModules, modulesPath, baseModules, options, config, ... }:
|
{ lib, extendModules, modulesPath, baseModules, options, config, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib) recursiveUpdate mkOption;
|
inherit (lib) recursiveUpdate mkOption mkDefault;
|
||||||
inherit (lib.my) mkBoolOpt';
|
inherit (lib.my) mkBoolOpt' dummyOption;
|
||||||
|
|
||||||
cfg = config.my.build;
|
cfg = config.my.build;
|
||||||
|
|
||||||
@ -17,19 +17,31 @@ let
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my = with lib.types; {
|
options = with lib.types; {
|
||||||
boot.isDevVM = mkBoolOpt' false "Whether the system is a development VM.";
|
my = {
|
||||||
build = options.system.build;
|
boot.isDevVM = mkBoolOpt' false "Whether the system is a development VM.";
|
||||||
asDevVM = mkOption {
|
build = options.system.build;
|
||||||
inherit (asDevVM) type;
|
asDevVM = mkOption {
|
||||||
default = { };
|
inherit (asDevVM) type;
|
||||||
visible = "shallow";
|
default = { };
|
||||||
description = "Configuration as a development VM";
|
visible = "shallow";
|
||||||
|
description = "Configuration as a development VM";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Forward declare options that won't exist until the VM module is actually imported
|
||||||
|
virtualisation = {
|
||||||
|
diskImage = dummyOption;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config.my.build = {
|
config = {
|
||||||
# The meta.mainProgram should probably be set upstream but oh well...
|
virtualisation = {
|
||||||
devVM = recursiveUpdate config.my.asDevVM.system.build.vm { meta.mainProgram = "run-${config.system.name}-vm"; };
|
diskImage = mkDefault "./.vms/${config.system.name}.qcow2";
|
||||||
|
};
|
||||||
|
my.build = {
|
||||||
|
# The meta.mainProgram should probably be set upstream but oh well...
|
||||||
|
devVM = recursiveUpdate config.my.asDevVM.system.build.vm { meta.mainProgram = "run-${config.system.name}-vm"; };
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
{ lib, pkgs, config, ... }:
|
{ lib, pkgs, config, ... }:
|
||||||
let
|
let
|
||||||
inherit (lib) concatStringsSep concatMap concatMapStringsSep mkIf mkDefault mkMerge mkVMOverride;
|
inherit (lib) optionalString concatStringsSep concatMap concatMapStringsSep mkIf mkDefault mkMerge mkVMOverride;
|
||||||
inherit (lib.my) mkOpt' mkBoolOpt' mkVMOverride' dummyOption;
|
inherit (lib.my) mkOpt' mkBoolOpt' mkVMOverride';
|
||||||
|
|
||||||
cfg = config.my.tmproot;
|
cfg = config.my.tmproot;
|
||||||
|
enablePersistence = cfg.persistDir != null;
|
||||||
|
|
||||||
showUnsaved =
|
showUnsaved =
|
||||||
''
|
''
|
||||||
@ -56,27 +57,18 @@ in
|
|||||||
options = with lib.types; {
|
options = with lib.types; {
|
||||||
my.tmproot = {
|
my.tmproot = {
|
||||||
enable = mkBoolOpt' true "Whether to enable tmproot.";
|
enable = mkBoolOpt' true "Whether to enable tmproot.";
|
||||||
persistDir = mkOpt' str "/persist" "Path where persisted files are stored.";
|
persistDir = mkOpt' (nullOr str) "/persist" "Path where persisted files are stored.";
|
||||||
size = mkOpt' str "2G" "Size of tmpfs root";
|
size = mkOpt' str "2G" "Size of tmpfs root";
|
||||||
unsaved = {
|
unsaved = {
|
||||||
showMotd = mkBoolOpt' true "Whether to show unsaved files with `dynamic-motd`.";
|
showMotd = mkBoolOpt' true "Whether to show unsaved files with `dynamic-motd`.";
|
||||||
ignore = mkOpt' (listOf str) [ ] "Path prefixes to ignore if unsaved.";
|
ignore = mkOpt' (listOf str) [ ] "Path prefixes to ignore if unsaved.";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Forward declare options that won't exist until the VM module is actually imported
|
|
||||||
virtualisation = {
|
|
||||||
diskImage = dummyOption;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable (mkMerge [
|
config = mkIf cfg.enable (mkMerge [
|
||||||
{
|
{
|
||||||
assertions = [
|
assertions = [
|
||||||
{
|
|
||||||
assertion = config.fileSystems ? "${cfg.persistDir}";
|
|
||||||
message = "The 'fileSystems' option does not specify your persistence file system (${cfg.persistDir}).";
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
# I mean you probably _could_, but if you're doing tmproot... come on
|
# I mean you probably _could_, but if you're doing tmproot... come on
|
||||||
assertion = !config.users.mutableUsers;
|
assertion = !config.users.mutableUsers;
|
||||||
@ -112,48 +104,6 @@ in
|
|||||||
(pkgs.writeScriptBin "tmproot-unsaved" showUnsaved)
|
(pkgs.writeScriptBin "tmproot-unsaved" showUnsaved)
|
||||||
];
|
];
|
||||||
|
|
||||||
# Catch non-existent source directories that are needed for boot (see `pathsNeededForBoot` in
|
|
||||||
# nixos/lib/util.nix). We do this by monkey-patching the `waitDevice` function that would otherwise hang.
|
|
||||||
boot.initrd.postDeviceCommands =
|
|
||||||
''
|
|
||||||
ensurePersistSource() {
|
|
||||||
[ -e "/mnt-root$1" ] && return
|
|
||||||
echo "Persistent source directory $1 does not exist, creating..."
|
|
||||||
install -dm "$2" "/mnt-root$1" || fail
|
|
||||||
}
|
|
||||||
|
|
||||||
_waitDevice() {
|
|
||||||
local device="$1"
|
|
||||||
|
|
||||||
${concatMapStringsSep " || \\\n " (d:
|
|
||||||
let
|
|
||||||
sourceDir = "${d.persistentStoragePath}${d.directory}";
|
|
||||||
in
|
|
||||||
''([ "$device" = "/mnt-root${sourceDir}" ] && ensurePersistSource "${sourceDir}" "${d.mode}")'')
|
|
||||||
config.environment.persistence."${cfg.persistDir}".directories}
|
|
||||||
|
|
||||||
waitDevice "$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
type waitDevice > /dev/null || (echo "waitDevice is missing!"; fail)
|
|
||||||
alias waitDevice=_waitDevice
|
|
||||||
'';
|
|
||||||
|
|
||||||
environment.persistence."${cfg.persistDir}" = {
|
|
||||||
hideMounts = mkDefault true;
|
|
||||||
directories = [
|
|
||||||
"/var/log"
|
|
||||||
# In theory we'd include only the files needed individually (i.e. the {U,G}ID map files that track deleted
|
|
||||||
# users and groups), but `update-users-groups.pl` actually deletes the original files for "atomic update".
|
|
||||||
# Also the script runs before impermanence does.
|
|
||||||
"/var/lib/nixos"
|
|
||||||
"/var/lib/systemd"
|
|
||||||
];
|
|
||||||
files = [
|
|
||||||
"/etc/machine-id"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
my.dynamic-motd.script = mkIf cfg.unsaved.showMotd
|
my.dynamic-motd.script = mkIf cfg.unsaved.showMotd
|
||||||
''
|
''
|
||||||
tmprootUnsaved() {
|
tmprootUnsaved() {
|
||||||
@ -163,8 +113,10 @@ in
|
|||||||
echo
|
echo
|
||||||
echo -e "\t\e[31;1;4mWarning:\e[0m $count file(s) on / will be lost on shutdown!"
|
echo -e "\t\e[31;1;4mWarning:\e[0m $count file(s) on / will be lost on shutdown!"
|
||||||
echo -e '\tTo see them, run `tmproot-unsaved` as root.'
|
echo -e '\tTo see them, run `tmproot-unsaved` as root.'
|
||||||
echo -e '\tAdd these files to `environment.persistence."${cfg.persistDir}"` to keep them!'
|
${optionalString enablePersistence ''
|
||||||
echo -e '\tOtherwise, they can be ignored by adding to `my.tmproot.unsaved.ignore`.'
|
echo -e '\tAdd these files to `environment.persistence."${cfg.persistDir}"` to keep them!'
|
||||||
|
''}
|
||||||
|
echo -e "\tIf they don't need to be kept, add them to \`my.tmproot.unsaved.ignore\`."
|
||||||
echo
|
echo
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,15 +124,8 @@ in
|
|||||||
'';
|
'';
|
||||||
|
|
||||||
fileSystems."/" = rootDef;
|
fileSystems."/" = rootDef;
|
||||||
|
|
||||||
virtualisation = {
|
|
||||||
diskImage = "./.vms/${config.system.name}-persist.qcow2";
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
(mkIf config.services.openssh.enable {
|
|
||||||
environment.persistence."${cfg.persistDir}".files =
|
|
||||||
concatMap (k: [ k.path "${k.path}.pub" ]) config.services.openssh.hostKeys;
|
|
||||||
})
|
|
||||||
(mkIf config.networking.resolvconf.enable {
|
(mkIf config.networking.resolvconf.enable {
|
||||||
my.tmproot.unsaved.ignore = [ "/etc/resolv.conf" ];
|
my.tmproot.unsaved.ignore = [ "/etc/resolv.conf" ];
|
||||||
})
|
})
|
||||||
@ -192,12 +137,77 @@ in
|
|||||||
|
|
||||||
fileSystems = mkVMOverride {
|
fileSystems = mkVMOverride {
|
||||||
"/" = mkVMOverride' rootDef;
|
"/" = mkVMOverride' rootDef;
|
||||||
# Hijack the "root" device for persistence in the VM
|
|
||||||
"${cfg.persistDir}" = {
|
|
||||||
device = config.virtualisation.bootDevice;
|
|
||||||
neededForBoot = true;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
|
(mkIf enablePersistence (mkMerge [
|
||||||
|
{
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = config.fileSystems ? "${cfg.persistDir}";
|
||||||
|
message = "The 'fileSystems' option does not specify your persistence file system (${cfg.persistDir}).";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
# Catch non-existent source directories that are needed for boot (see `pathsNeededForBoot` in
|
||||||
|
# nixos/lib/util.nix). We do this by monkey-patching the `waitDevice` function that would otherwise hang.
|
||||||
|
boot.initrd.postDeviceCommands =
|
||||||
|
''
|
||||||
|
ensurePersistSource() {
|
||||||
|
[ -e "/mnt-root$1" ] && return
|
||||||
|
echo "Persistent source directory $1 does not exist, creating..."
|
||||||
|
install -dm "$2" "/mnt-root$1" || fail
|
||||||
|
}
|
||||||
|
|
||||||
|
_waitDevice() {
|
||||||
|
local device="$1"
|
||||||
|
|
||||||
|
${concatMapStringsSep " || \\\n " (d:
|
||||||
|
let
|
||||||
|
sourceDir = "${d.persistentStoragePath}${d.directory}";
|
||||||
|
in
|
||||||
|
''([ "$device" = "/mnt-root${sourceDir}" ] && ensurePersistSource "${sourceDir}" "${d.mode}")'')
|
||||||
|
config.environment.persistence."${cfg.persistDir}".directories}
|
||||||
|
|
||||||
|
waitDevice "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
type waitDevice > /dev/null || (echo "waitDevice is missing!"; fail)
|
||||||
|
alias waitDevice=_waitDevice
|
||||||
|
'';
|
||||||
|
|
||||||
|
environment.persistence."${cfg.persistDir}" = {
|
||||||
|
hideMounts = mkDefault true;
|
||||||
|
directories = [
|
||||||
|
"/var/log"
|
||||||
|
# In theory we'd include only the files needed individually (i.e. the {U,G}ID map files that track deleted
|
||||||
|
# users and groups), but `update-users-groups.pl` actually deletes the original files for "atomic update".
|
||||||
|
# Also the script runs before impermanence does.
|
||||||
|
"/var/lib/nixos"
|
||||||
|
"/var/lib/systemd"
|
||||||
|
];
|
||||||
|
files = [
|
||||||
|
"/etc/machine-id"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
virtualisation = {
|
||||||
|
diskImage = "./.vms/${config.system.name}-persist.qcow2";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
(mkIf config.services.openssh.enable {
|
||||||
|
environment.persistence."${cfg.persistDir}".files =
|
||||||
|
concatMap (k: [ k.path "${k.path}.pub" ]) config.services.openssh.hostKeys;
|
||||||
|
})
|
||||||
|
(mkIf config.my.boot.isDevVM {
|
||||||
|
fileSystems = mkVMOverride {
|
||||||
|
# Hijack the "root" device for persistence in the VM
|
||||||
|
"${cfg.persistDir}" = {
|
||||||
|
device = config.virtualisation.bootDevice;
|
||||||
|
neededForBoot = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
]))
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
{ lib, pkgsFlakes, inputs, modules }:
|
{ lib, pkgsFlakes, inputs, modules }:
|
||||||
let
|
let
|
||||||
inherit (builtins) attrValues mapAttrs;
|
inherit (builtins) attrValues mapAttrs;
|
||||||
inherit (lib) mkDefault;
|
inherit (lib) optionals mkDefault;
|
||||||
|
|
||||||
mkSystem =
|
mkSystem =
|
||||||
name: {
|
name: {
|
||||||
system,
|
system,
|
||||||
nixpkgs ? "unstable",
|
nixpkgs ? "unstable",
|
||||||
config,
|
config,
|
||||||
|
docCustom ? true,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
pkgsFlake = pkgsFlakes.${nixpkgs};
|
pkgsFlake = pkgsFlakes.${nixpkgs};
|
||||||
@ -31,8 +32,8 @@ let
|
|||||||
inputs.impermanence.nixosModule
|
inputs.impermanence.nixosModule
|
||||||
inputs.agenix.nixosModules.age
|
inputs.agenix.nixosModules.age
|
||||||
inputs.home-manager.nixosModule
|
inputs.home-manager.nixosModule
|
||||||
] ++ modules;
|
] ++ (optionals docCustom modules);
|
||||||
modules = [
|
modules = (optionals (!docCustom) modules) ++ [
|
||||||
{
|
{
|
||||||
_module.args = { inherit system inputs; };
|
_module.args = { inherit system inputs; };
|
||||||
system.name = name;
|
system.name = name;
|
||||||
|
Loading…
Reference in New Issue
Block a user