VMs: persist state folders on host
Done:
- move vm inspect attrs from system.clan.vm.config to clanCore.vm.inspect. This gives us proper name and type checking. everything in `system` is basically freeform, so the previous option definitions were never enforced
- when running VMs, mount state directory from ~/.config/clan/vmstate/{...} from the host to /var/vmstate inside the vm
- create bind mount inside the VM from /var/vmstate/{folder} to / for all folders defined in clanCore.state.<name>.folders
TODOs:
- make sure directories in ~/.config/clan/vmstate never collide (include hash of clan-url, etc.)
- port impure test to python
This commit is contained in:
@@ -45,12 +45,6 @@
|
||||
'';
|
||||
default = "${pkgs.coreutils}/bin/true";
|
||||
};
|
||||
vm.config = lib.mkOption {
|
||||
type = lib.types.attrs;
|
||||
description = ''
|
||||
the vm config
|
||||
'';
|
||||
};
|
||||
vm.create = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
description = ''
|
||||
|
||||
@@ -1,19 +1,59 @@
|
||||
{ lib, config, pkgs, options, extendModules, modulesPath, ... }:
|
||||
let
|
||||
vmConfig = extendModules {
|
||||
modules = [
|
||||
# Generates a fileSystems entry for bind mounting a given state folder path
|
||||
# It binds directories from /var/clanstate/{some-path} to /{some-path}.
|
||||
# As a result, all state paths will be persisted across reboots, because
|
||||
# the state folder is mounted from the host system.
|
||||
mkBindMount = path: {
|
||||
name = path;
|
||||
value = {
|
||||
device = "/var/clanstate/${path}";
|
||||
options = [ "bind" ];
|
||||
};
|
||||
};
|
||||
|
||||
# Flatten the list of state folders into a single list
|
||||
stateFolders = lib.flatten (
|
||||
lib.mapAttrsToList
|
||||
(_item: attrs: attrs.folders)
|
||||
config.clanCore.state
|
||||
);
|
||||
|
||||
# A module setting up bind mounts for all state folders
|
||||
stateMounts = {
|
||||
virtualisation.fileSystems =
|
||||
lib.listToAttrs
|
||||
(map mkBindMount stateFolders);
|
||||
};
|
||||
|
||||
vmModule = {
|
||||
imports = [
|
||||
(modulesPath + "/virtualisation/qemu-vm.nix")
|
||||
./serial.nix
|
||||
{
|
||||
virtualisation.fileSystems.${config.clanCore.secretsUploadDirectory} = lib.mkForce {
|
||||
device = "secrets";
|
||||
fsType = "9p";
|
||||
neededForBoot = true;
|
||||
options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ];
|
||||
};
|
||||
boot.initrd.systemd.enable = true;
|
||||
}
|
||||
stateMounts
|
||||
];
|
||||
virtualisation.fileSystems = {
|
||||
${config.clanCore.secretsUploadDirectory} = lib.mkForce {
|
||||
device = "secrets";
|
||||
fsType = "9p";
|
||||
neededForBoot = true;
|
||||
options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ];
|
||||
};
|
||||
"/var/clanstate" = {
|
||||
device = "state";
|
||||
fsType = "9p";
|
||||
options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ];
|
||||
};
|
||||
};
|
||||
boot.initrd.systemd.enable = true;
|
||||
};
|
||||
|
||||
# We cannot simply merge the VM config into the current system config, because
|
||||
# it is not necessarily a VM.
|
||||
# Instead we use extendModules to create a second instance of the current
|
||||
# system configuration, and then merge the VM config into that.
|
||||
vmConfig = extendModules {
|
||||
modules = [ vmModule stateMounts ];
|
||||
};
|
||||
in
|
||||
{
|
||||
@@ -47,17 +87,54 @@ in
|
||||
'';
|
||||
};
|
||||
};
|
||||
# All important VM config variables needed by the vm runner
|
||||
# this is really just a remapping of values defined elsewhere
|
||||
# and therefore not intended to be set by the user
|
||||
clanCore.vm.inspect = {
|
||||
clan_name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
description = ''
|
||||
the name of the clan
|
||||
'';
|
||||
};
|
||||
memory_size = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
description = ''
|
||||
the amount of memory to allocate to the vm
|
||||
'';
|
||||
};
|
||||
cores = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
description = ''
|
||||
the number of cores to allocate to the vm
|
||||
'';
|
||||
};
|
||||
graphics = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
description = ''
|
||||
whether to enable graphics for the vm
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
# for clan vm inspect
|
||||
clanCore.vm.inspect = {
|
||||
clan_name = config.clanCore.clanName;
|
||||
memory_size = config.clan.virtualisation.memorySize;
|
||||
inherit (config.clan.virtualisation) cores graphics;
|
||||
};
|
||||
# for clan vm create
|
||||
system.clan.vm = {
|
||||
# for clan vm inspect
|
||||
config = {
|
||||
clan_name = config.clanCore.clanName;
|
||||
memory_size = config.clan.virtualisation.memorySize;
|
||||
inherit (config.clan.virtualisation) cores graphics;
|
||||
};
|
||||
# for clan vm create
|
||||
create = pkgs.writeText "vm.json" (builtins.toJSON {
|
||||
initrd = "${vmConfig.config.system.build.initialRamdisk}/${vmConfig.config.system.boot.loader.initrdFile}";
|
||||
toplevel = vmConfig.config.system.build.toplevel;
|
||||
|
||||
Reference in New Issue
Block a user