{ config, self, lib, inputs, options, pkgs, ... }: with lib; let cfg = config.services.malobeo.microvm; in { options = { services.malobeo.microvm = { enableHostBridge = mkOption { default = false; type = types.bool; description = lib.mdDoc "Setup bridge device for microvms."; }; testHost = mkOption { default = false; type = types.bool; description = lib.mdDoc "Enable when the host is used for development and testing using run-vm"; }; interface = mkOption { default = "eno1"; type = types.str; }; gateway = mkOption { default = "10.0.0.1"; type = types.str; }; address = mkOption { default = "10.0.0.1/24"; type = types.str; }; dns = mkOption { default = [ "1.1.1.1" ]; type = types.listOf types.str; }; enableHostBridgeUnstable = mkOption { default = false; type = types.bool; description = lib.mdDoc "Setup bridge device for microvms."; }; deployHosts = mkOption { default = []; type = types.listOf types.str; description = '' List hostnames of MicroVMs that should be automatically initializes and autostart ''; }; }; }; imports = [ inputs.microvm.nixosModules.host ]; config = { assertions = [ { assertion = !(cfg.enableHostBridgeUnstable && cfg.enableHostBridge); message = '' Only enableHostBridge or enableHostBridgeUnstable! Not Both! ''; } ]; systemd.network = mkIf (cfg.enableHostBridge || cfg.enableHostBridgeUnstable) { enable = true; networks."10-lan" = { matchConfig.Name = ["vm-*"] ++ (if !cfg.testHost then [ "${cfg.interface}" ] else [ ]); networkConfig = { Bridge = "malobeo0"; }; }; netdevs."malobeo0" = { netdevConfig = { Name = "malobeo0"; Kind = "bridge"; }; }; networks."10-lan-bridge" = if !cfg.testHost then { matchConfig.Name = "malobeo0"; networkConfig = { Address = [ "${cfg.address}" ]; Gateway = "${cfg.gateway}"; DNS = cfg.dns; IPv6AcceptRA = true; }; linkConfig.RequiredForOnline = "routable"; } else { matchConfig.Name = "malobeo0"; networkConfig = { DHCPServer = true; IPv6SendRA = true; }; addresses = if cfg.enableHostBridgeUnstable then [ { Address = "10.0.0.1/24"; } ] else [ { Address = "10.0.0.1/24"; } ]; }; }; microvm.vms = let # Map the values to each hostname to then generate an Attrset using listToAttrs mapperFunc = name: { inherit name; value = { # Host build-time reference to where the MicroVM NixOS is defined # under nixosConfigurations flake = inputs.malobeo; # Specify from where to let `microvm -u` update later on updateFlake = "git+https://git.dynamicdiscord.de/malobeo/infrastructure"; }; }; in builtins.listToAttrs (map mapperFunc cfg.deployHosts); systemd.services = builtins.foldl' (services: name: services // { "microvm-update@${name}" = { description = "Update MicroVMs automatically"; after = [ "network-online.target" ]; wants = [ "network-online.target" ]; unitConfig.ConditionPathExists = "/var/lib/microvms/${name}"; serviceConfig = { LimitNOFILE = "1048576"; Type = "oneshot"; }; path = with pkgs; [ nix git ]; environment.HOME = config.users.users.root.home; script = '' /run/current-system/sw/bin/microvm -Ru ${name} ''; }; "microvm-init-dirs@${name}" = { description = "Initialize microvm directories"; after = [ "zfs-mount.service" ]; wantedBy = [ "microvm@${name}.service" ]; unitConfig.ConditionPathExists = "!/var/lib/microvms/${name}/.is_initialized"; serviceConfig = { Type = "oneshot"; }; script = '' mkdir -p /var/lib/microvms/${name}/var mkdir -p /var/lib/microvms/${name}/etc mkdir -p /var/lib/microvms/data/${name} touch /var/lib/microvms/${name}/.is_initialized ''; }; }) {} (cfg.deployHosts); systemd.timers = builtins.foldl' (timers: name: timers // { "microvm-update-${name}" = { wantedBy = [ "timers.target" ]; timerConfig = { Unit = "microvm-update@${name}.service"; # three times per hour OnCalendar = "*:0,20,40:00"; Persistent = true; }; }; }) {} (cfg.deployHosts); }; }