1
1
Fork 1
mirror of https://github.com/oddlama/nixos-extra-modules.git synced 2025-10-10 22:00:39 +02:00

feat: upstream node generation

This commit is contained in:
Patrick 2025-02-06 21:14:42 +01:00
parent f1a3f0303b
commit 93b08971cf
No known key found for this signature in database
GPG key ID: 451F95EFB8BECD0F
9 changed files with 268 additions and 48 deletions

View file

@ -0,0 +1,6 @@
{
imports = [
./globals.nix
./nodes.nix
];
}

74
flake-modules/globals.nix Normal file
View file

@ -0,0 +1,74 @@
{
inputs,
lib,
config,
...
}:
let
inherit (lib) mkOption types;
in
{
options.globals = {
optModules = mkOption {
type = types.listOf types.deferredModule;
default = [ ];
description = ''
Modules defining global options.
These should not include any config only option declaration.
Will be included in the exported nixos Modules from this flake to be included
into the host evaluation.
'';
};
defModules = mkOption {
type = types.listOf types.deferredModule;
default = [ ];
description = ''
Modules configuring global options.
These should not include any option declaration use {option}`optModules` for that.
Will not included in the exported nixos Modules.
'';
};
attrkeys = mkOption {
type = types.listOf types.str;
default = [ ];
description = ''
The toplevel attrNames for your globals.
Make sure the keys of this attrset are trivially evaluatable to avoid infinite recursion,
therefore we inherit relevant attributes from the config.
'';
};
};
config.flake = flakeSubmod: {
globals =
let
globalsSystem = lib.evalModules {
prefix = [ "globals" ];
specialArgs = {
inherit (inputs.self.pkgs.x86_64-linux) lib;
inherit inputs;
inherit (flakeSubmod.config) nodes;
};
modules =
config.globals.optModules
++ config.globals.defModules
++ [
../modules/globals.nix
(
{ lib, ... }:
{
globals = lib.mkMerge (
lib.concatLists (
lib.flip lib.mapAttrsToList flakeSubmod.config.nodes (
name: cfg:
builtins.addErrorContext "while aggregating globals from nixosConfigurations.${name} into flake-level globals:" cfg.config._globalsDefs
)
)
);
}
)
];
};
in
lib.genAttrs config.globals.attrkeys (x: globalsSystem.config.globals.${x});
};
}

110
flake-modules/nodes.nix Normal file
View file

@ -0,0 +1,110 @@
{
inputs,
self,
lib,
config,
...
}:
let
inherit (lib) mkOption types;
topConfig = config;
in
{
options.node = {
path = mkOption {
type = types.path;
description = "The path containing your host definitions";
};
nixpkgs = mkOption {
type = types.path;
default = inputs.nixpkgs;
description = "The path to your nixpkgs.";
};
};
config.flake =
{
config,
lib,
...
}:
let
inherit (lib)
concatMapAttrs
filterAttrs
flip
genAttrs
mapAttrs'
nameValuePair
;
# Creates a new nixosSystem with the correct specialArgs, pkgs and name definition
mkHost =
{ minimal }:
name:
let
pkgs = config.pkgs.x86_64-linux;
in
(import "${topConfig.node.nixpkgs}/nixos/lib/eval-config.nix") {
system = null;
specialArgs = {
# Use the correct instance lib that has our overlays
inherit (pkgs) lib;
inherit (config) nodes globals;
inherit minimal;
extraModules = [
../modules
] ++ topConfig.globals.optModules;
inputs = inputs // {
inherit (topConfig.node) nixpkgs;
};
};
modules = [
(
{ config, ... }:
{
node.name = name;
node.secretsDir = topConfig.node.path + "/${name}/secrets";
nixpkgs.pkgs = self.pkgs.${config.nixpkgs.hostPlatform.system};
}
)
(topConfig.node.path + "/${name}")
../modules
] ++ topConfig.globals.optModules;
};
# Load the list of hosts that this flake defines, which
# associates the minimum amount of metadata that is necessary
# to instanciate hosts correctly.
hosts = builtins.attrNames (
filterAttrs (_: type: type == "directory") (builtins.readDir topConfig.node.path)
);
in
# Process each nixosHosts declaration and generatea nixosSystem definitions
{
nixosConfigurations = genAttrs hosts (mkHost {
minimal = false;
});
minimalConfigurations = genAttrs hosts (mkHost {
minimal = true;
});
# True NixOS nodes can define additional guest nodes that are built
# together with it. We collect all defined guests from each node here
# to allow accessing any node via the unified attribute `nodes`.
guestConfigurations = flip concatMapAttrs config.nixosConfigurations (
_: node:
flip mapAttrs' (node.config.guests or { }) (
guestName: guestDef:
nameValuePair guestDef.nodeName (
if guestDef.backend == "microvm" then
node.config.microvm.vms.${guestName}.config
else
node.config.containers.${guestName}.nixosConfiguration
)
)
);
# All nixosSystem instanciations are collected here, so that we can refer
# to any system via nodes.<name>
nodes = config.nixosConfigurations // config.guestConfigurations;
};
}

View file

@ -35,6 +35,10 @@
];
flake.modules = {
flake = {
nixos-extra-modules = import ./flake-modules;
default = self.modules.flake.nixos-extra-modules;
};
nixos = {
nixos-extra-modules = import ./modules;
default = self.modules.nixos.nixos-extra-modules;

View file

@ -1,8 +1,10 @@
{inputs, ...}: {
{ inputs, ... }:
{
imports = [
inputs.microvm.nixosModules.host
./boot.nix
./globals.nix
./guests/default.nix
./interface-naming.nix
./nginx.nix

9
modules/globals.nix Normal file
View file

@ -0,0 +1,9 @@
{ lib, options, ... }:
{
options._globalsDefs = lib.mkOption {
type = lib.types.unspecified;
default = options.globals.definitions;
readOnly = true;
internal = true;
};
}

View file

@ -1,12 +1,14 @@
_guestName: guestCfg: {lib, ...}: let
inherit
(lib)
_guestName: guestCfg:
{ lib, ... }:
let
inherit (lib)
mkForce
nameValuePair
listToAttrs
flip
;
in {
in
{
node.name = guestCfg.nodeName;
node.type = guestCfg.backend;
@ -20,20 +22,20 @@ in {
systemd.network.networks = listToAttrs (
flip map guestCfg.networking.links (
name:
nameValuePair "10-${name}" {
matchConfig.Name = name;
DHCP = "yes";
# XXX: Do we really want this?
dhcpV4Config.UseDNS = false;
dhcpV6Config.UseDNS = false;
ipv6AcceptRAConfig.UseDNS = false;
networkConfig = {
IPv6PrivacyExtensions = "yes";
MulticastDNS = true;
IPv6AcceptRA = true;
};
linkConfig.RequiredForOnline = "routable";
}
nameValuePair "10-${name}" {
matchConfig.Name = name;
DHCP = "yes";
# XXX: Do we really want this?
dhcpV4Config.UseDNS = false;
dhcpV6Config.UseDNS = false;
ipv6AcceptRAConfig.UseDNS = false;
networkConfig = {
IPv6PrivacyExtensions = "yes";
MulticastDNS = true;
IPv6AcceptRA = true;
};
linkConfig.RequiredForOnline = "routable";
}
)
);
}

View file

@ -1,17 +1,20 @@
guestName: guestCfg: {
guestName: guestCfg:
{
config,
inputs,
lib,
pkgs,
extraModules,
...
}: let
inherit
(lib)
}:
let
inherit (lib)
flip
mapAttrs'
nameValuePair
;
in {
in
{
inherit (guestCfg.container) macvlans;
ephemeral = true;
privateNetwork = true;
@ -21,10 +24,10 @@ in {
];
bindMounts = flip mapAttrs' guestCfg.zfs (
_: zfsCfg:
nameValuePair zfsCfg.guestMountpoint {
hostPath = zfsCfg.hostMountpoint;
isReadOnly = false;
}
nameValuePair zfsCfg.guestMountpoint {
hostPath = zfsCfg.hostMountpoint;
isReadOnly = false;
}
);
nixosConfiguration = (import "${inputs.nixpkgs}/nixos/lib/eval-config.nix") {
specialArgs = guestCfg.extraSpecialArgs;
@ -55,16 +58,17 @@ in {
# to the state fs).
fileSystems = flip mapAttrs' guestCfg.zfs (
_: zfsCfg:
nameValuePair zfsCfg.guestMountpoint {
neededForBoot = true;
fsType = "none";
device = zfsCfg.guestMountpoint;
options = ["bind"];
}
nameValuePair zfsCfg.guestMountpoint {
neededForBoot = true;
fsType = "none";
device = zfsCfg.guestMountpoint;
options = [ "bind" ];
}
);
}
(import ./common-guest-config.nix guestName guestCfg)
]
++ guestCfg.modules;
++ guestCfg.modules
++ extraModules;
};
}

View file

@ -1,10 +1,12 @@
guestName: guestCfg: {
guestName: guestCfg:
{
inputs,
lib,
extraModules,
...
}: let
inherit
(lib)
}:
let
inherit (lib)
concatMapAttrs
flip
mapAttrs
@ -13,19 +15,22 @@ guestName: guestCfg: {
mkForce
replaceStrings
;
in {
in
{
specialArgs = guestCfg.extraSpecialArgs;
pkgs = inputs.self.pkgs.${guestCfg.microvm.system};
inherit (guestCfg) autostart;
config = {
imports =
guestCfg.modules
extraModules
++ guestCfg.modules
++ [
(import ./common-guest-config.nix guestName guestCfg)
(
{config, ...}: {
{ config, ... }:
{
# Set early hostname too, so we can associate those logs to this host and don't get "localhost" entries in loki
boot.kernelParams = ["systemd.hostname=${config.networking.hostName}"];
boot.kernelParams = [ "systemd.hostname=${config.networking.hostName}" ];
}
)
];
@ -47,13 +52,15 @@ in {
# MACVTAP bridge to the host's network
interfaces = flip mapAttrsToList guestCfg.microvm.interfaces (
_: {
_:
{
mac,
hostLink,
...
}: {
}:
{
type = "macvtap";
id = "vm-${replaceStrings [":"] [""] mac}";
id = "vm-${replaceStrings [ ":" ] [ "" ] mac}";
inherit mac;
macvtap = {
link = hostLink;
@ -82,9 +89,11 @@ in {
);
};
networking.renameInterfacesByMac = flip mapAttrs guestCfg.microvm.interfaces (_: {mac, ...}: mac);
networking.renameInterfacesByMac = flip mapAttrs guestCfg.microvm.interfaces (_: { mac, ... }: mac);
systemd.network.networks = flip concatMapAttrs guestCfg.microvm.interfaces (
name: {mac, ...}: {
name:
{ mac, ... }:
{
"10-${name}".matchConfig = mkForce {
MACAddress = mac;
};