forked from mirrors_public/oddlama_nix-config
feat: allow multiple zfs dataset definitions for container
This commit is contained in:
parent
b4c7fbd0e8
commit
8f28273b65
6 changed files with 141 additions and 132 deletions
|
@ -58,22 +58,26 @@
|
||||||
#};
|
#};
|
||||||
|
|
||||||
guests = let
|
guests = let
|
||||||
mkGuest = mainModule: {
|
mkGuest = guestName: {
|
||||||
autostart = true;
|
autostart = true;
|
||||||
zfs = {
|
zfs."/state" = {
|
||||||
enable = true;
|
|
||||||
pool = "rpool";
|
pool = "rpool";
|
||||||
|
dataset = "local/guests/${guestName}";
|
||||||
|
};
|
||||||
|
zfs."/persist" = {
|
||||||
|
pool = "rpool";
|
||||||
|
dataset = "safe/guests/${guestName}";
|
||||||
};
|
};
|
||||||
modules = [
|
modules = [
|
||||||
../../modules
|
../../modules
|
||||||
./guests/common.nix
|
./guests/common.nix
|
||||||
({config, ...}: {node.secretsDir = ./secrets + "/${config.node.guestName}";})
|
./guests/${guestName}.nix
|
||||||
mainModule
|
{node.secretsDir = ./secrets/${guestName};}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
mkMicrovm = system: mainModule:
|
mkMicrovm = system: guestName:
|
||||||
mkGuest mainModule
|
mkGuest guestName
|
||||||
// {
|
// {
|
||||||
backend = "microvm";
|
backend = "microvm";
|
||||||
microvm = {
|
microvm = {
|
||||||
|
@ -82,23 +86,26 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
mkContainer = mainModule:
|
mkContainer = guestName: {
|
||||||
mkGuest mainModule
|
${guestName} =
|
||||||
// {
|
mkGuest guestName
|
||||||
backend = "container";
|
// {
|
||||||
container.macvlan = "lan";
|
backend = "container";
|
||||||
};
|
container.macvlan = "lan";
|
||||||
in
|
};
|
||||||
lib.mkIf (!minimal) {
|
|
||||||
adguardhome = mkContainer ./guests/adguardhome.nix;
|
|
||||||
forgejo = mkContainer ./guests/forgejo.nix;
|
|
||||||
grafana = mkContainer ./guests/grafana.nix;
|
|
||||||
influxdb = mkContainer ./guests/influxdb.nix;
|
|
||||||
kanidm = mkContainer ./guests/kanidm.nix;
|
|
||||||
loki = mkContainer ./guests/loki.nix;
|
|
||||||
paperless = mkContainer ./guests/paperless.nix;
|
|
||||||
vaultwarden = mkContainer ./guests/vaultwarden.nix;
|
|
||||||
};
|
};
|
||||||
|
in
|
||||||
|
lib.mkIf (!minimal) (
|
||||||
|
{}
|
||||||
|
// mkContainer "adguardhome"
|
||||||
|
// mkContainer "forgejo"
|
||||||
|
// mkContainer "grafana"
|
||||||
|
// mkContainer "influxdb"
|
||||||
|
// mkContainer "kanidm"
|
||||||
|
// mkContainer "loki"
|
||||||
|
// mkContainer "paperless"
|
||||||
|
// mkContainer "vaultwarden"
|
||||||
|
);
|
||||||
|
|
||||||
#ddclient = defineVm;
|
#ddclient = defineVm;
|
||||||
#samba+wsdd = defineVm;
|
#samba+wsdd = defineVm;
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
datasets =
|
datasets =
|
||||||
defaultZfsDatasets
|
defaultZfsDatasets
|
||||||
// {
|
// {
|
||||||
"safe/vms" = unmountable;
|
"safe/guests" = unmountable;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
guestName: guestCfg: {lib, ...}: let
|
_guestName: guestCfg: {lib, ...}: let
|
||||||
inherit (lib) mkForce;
|
inherit (lib) mkForce;
|
||||||
in {
|
in {
|
||||||
node.guestName = guestName;
|
|
||||||
node.name = guestCfg.nodeName;
|
node.name = guestCfg.nodeName;
|
||||||
node.type = guestCfg.backend;
|
node.type = guestCfg.backend;
|
||||||
|
|
||||||
|
|
|
@ -6,25 +6,25 @@ guestName: guestCfg: {
|
||||||
nodes,
|
nodes,
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}: {
|
}: let
|
||||||
|
inherit
|
||||||
|
(lib)
|
||||||
|
mapAttrs'
|
||||||
|
flip
|
||||||
|
nameValuePair
|
||||||
|
;
|
||||||
|
in {
|
||||||
autoStart = guestCfg.autostart;
|
autoStart = guestCfg.autostart;
|
||||||
macvlans = ["${guestCfg.container.macvlan}:${guestCfg.networking.mainLinkName}"];
|
macvlans = ["${guestCfg.container.macvlan}:${guestCfg.networking.mainLinkName}"];
|
||||||
ephemeral = true;
|
ephemeral = true;
|
||||||
privateNetwork = true;
|
privateNetwork = true;
|
||||||
# We bind-mount stuff from the host into /guest first, and later bind
|
bindMounts = flip mapAttrs' guestCfg.zfs (
|
||||||
# mount them into the correct path inside the guest, so we have a
|
_: zfsCfg:
|
||||||
# fileSystems entry that impermanence can depend upon.
|
nameValuePair zfsCfg.guestMountpoint {
|
||||||
bindMounts = {
|
hostPath = zfsCfg.hostMountpoint;
|
||||||
"/guest/state" = {
|
isReadOnly = false;
|
||||||
hostPath = "/state/guests/${guestName}";
|
}
|
||||||
isReadOnly = false;
|
);
|
||||||
};
|
|
||||||
# Mount persistent data from the host
|
|
||||||
"/guest/persist" = lib.mkIf guestCfg.zfs.enable {
|
|
||||||
hostPath = guestCfg.zfs.mountpoint;
|
|
||||||
isReadOnly = false;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
nixosConfiguration = inputs.nixpkgs.lib.nixosSystem {
|
nixosConfiguration = inputs.nixpkgs.lib.nixosSystem {
|
||||||
specialArgs = {
|
specialArgs = {
|
||||||
inherit lib nodes inputs minimal;
|
inherit lib nodes inputs minimal;
|
||||||
|
@ -46,18 +46,17 @@ guestName: guestCfg: {
|
||||||
nixpkgs.config = pkgs.config;
|
nixpkgs.config = pkgs.config;
|
||||||
|
|
||||||
# Bind the /guest/* paths from above so impermancence doesn't complain.
|
# Bind the /guest/* paths from above so impermancence doesn't complain.
|
||||||
fileSystems."/state" = {
|
# We bind-mount stuff from the host to itself, which is perfectly defined
|
||||||
fsType = "none";
|
# and not recursive. This allows us to have a fileSystems entry for each
|
||||||
neededForBoot = true;
|
# bindMount which other stuff can depend upon (impermanence adds dependencies
|
||||||
device = "/guest/state";
|
# to the state fs).
|
||||||
options = ["bind"];
|
fileSystems = flip mapAttrs' guestCfg.zfs (_: zfsCfg:
|
||||||
};
|
nameValuePair zfsCfg.guestMountpoint {
|
||||||
fileSystems."/persist" = lib.mkIf guestCfg.zfs.enable {
|
neededForBoot = true;
|
||||||
fsType = "none";
|
fsType = "none";
|
||||||
neededForBoot = true;
|
device = zfsCfg.guestMountpoint;
|
||||||
device = "/guest/persist";
|
options = ["bind"];
|
||||||
options = ["bind"];
|
});
|
||||||
};
|
|
||||||
}
|
}
|
||||||
(import ./common-guest-config.nix guestName guestCfg)
|
(import ./common-guest-config.nix guestName guestCfg)
|
||||||
]
|
]
|
||||||
|
|
|
@ -14,8 +14,9 @@
|
||||||
escapeShellArg
|
escapeShellArg
|
||||||
makeBinPath
|
makeBinPath
|
||||||
mapAttrsToList
|
mapAttrsToList
|
||||||
|
mkMerge
|
||||||
mergeToplevelConfigs
|
mergeToplevelConfigs
|
||||||
mkEnableOption
|
flip
|
||||||
mkIf
|
mkIf
|
||||||
mkOption
|
mkOption
|
||||||
types
|
types
|
||||||
|
@ -26,58 +27,57 @@
|
||||||
# Configuration required on the host for a specific guest
|
# Configuration required on the host for a specific guest
|
||||||
defineGuest = guestName: guestCfg: {
|
defineGuest = guestName: guestCfg: {
|
||||||
# Add the required datasets to the disko configuration of the machine
|
# Add the required datasets to the disko configuration of the machine
|
||||||
disko.devices.zpool = mkIf guestCfg.zfs.enable {
|
disko.devices.zpool = mkMerge (flip map (attrValues guestCfg.zfs) (zfsCfg: {
|
||||||
${guestCfg.zfs.pool}.datasets.${guestCfg.zfs.dataset} =
|
${zfsCfg.pool}.datasets.${zfsCfg.dataset} =
|
||||||
disko.zfs.filesystem guestCfg.zfs.mountpoint;
|
disko.zfs.filesystem zfsCfg.hostMountpoint;
|
||||||
};
|
}));
|
||||||
|
|
||||||
# Ensure that the zfs dataset exists before it is mounted.
|
# Ensure that the zfs dataset exists before it is mounted.
|
||||||
systemd.services = let
|
systemd.services = mkMerge (flip map (attrValues guestCfg.zfs) (zfsCfg: let
|
||||||
fsMountUnit = "${utils.escapeSystemdPath guestCfg.zfs.mountpoint}.mount";
|
fsMountUnit = "${utils.escapeSystemdPath zfsCfg.hostMountpoint}.mount";
|
||||||
in
|
in {
|
||||||
mkIf guestCfg.zfs.enable {
|
# Ensure that the zfs dataset exists before it is mounted.
|
||||||
# Ensure that the zfs dataset exists before it is mounted.
|
"zfs-ensure-${utils.escapeSystemdPath zfsCfg.hostMountpoint}" = {
|
||||||
"zfs-ensure-${utils.escapeSystemdPath guestCfg.zfs.mountpoint}" = {
|
wantedBy = [fsMountUnit];
|
||||||
wantedBy = [fsMountUnit];
|
before = [fsMountUnit];
|
||||||
before = [fsMountUnit];
|
after = [
|
||||||
after = [
|
"zfs-import-${utils.escapeSystemdPath zfsCfg.pool}.service"
|
||||||
"zfs-import-${utils.escapeSystemdPath guestCfg.zfs.pool}.service"
|
"zfs-mount.target"
|
||||||
"zfs-mount.target"
|
];
|
||||||
];
|
unitConfig.DefaultDependencies = "no";
|
||||||
unitConfig.DefaultDependencies = "no";
|
serviceConfig.Type = "oneshot";
|
||||||
serviceConfig.Type = "oneshot";
|
script = let
|
||||||
script = let
|
poolDataset = "${zfsCfg.pool}/${zfsCfg.dataset}";
|
||||||
poolDataset = "${guestCfg.zfs.pool}/${guestCfg.zfs.dataset}";
|
diskoDataset = config.disko.devices.zpool.${zfsCfg.pool}.datasets.${zfsCfg.dataset};
|
||||||
diskoDataset = config.disko.devices.zpool.${guestCfg.zfs.pool}.datasets.${guestCfg.zfs.dataset};
|
in ''
|
||||||
in ''
|
export PATH=${makeBinPath [pkgs.zfs]}":$PATH"
|
||||||
export PATH=${makeBinPath [pkgs.zfs]}":$PATH"
|
if ! zfs list -H -o type ${escapeShellArg poolDataset} &>/dev/null ; then
|
||||||
if ! zfs list -H -o type ${escapeShellArg poolDataset} &>/dev/null ; then
|
${diskoDataset._create}
|
||||||
${diskoDataset._create}
|
fi
|
||||||
fi
|
'';
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
# Ensure that the zfs dataset has the correct permissions when mounted
|
|
||||||
"zfs-chown-${utils.escapeSystemdPath guestCfg.zfs.mountpoint}" = {
|
|
||||||
after = [fsMountUnit];
|
|
||||||
unitConfig.DefaultDependencies = "no";
|
|
||||||
serviceConfig.Type = "oneshot";
|
|
||||||
script = ''
|
|
||||||
chmod 700 ${escapeShellArg guestCfg.zfs.mountpoint}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
"microvm@${guestName}" = mkIf (guestCfg.backend == "microvm") {
|
|
||||||
requires = [fsMountUnit "zfs-chown-${utils.escapeSystemdPath guestCfg.zfs.mountpoint}.service"];
|
|
||||||
after = [fsMountUnit "zfs-chown-${utils.escapeSystemdPath guestCfg.zfs.mountpoint}.service"];
|
|
||||||
};
|
|
||||||
|
|
||||||
"container@${guestName}" = mkIf (guestCfg.backend == "container") {
|
|
||||||
requires = [fsMountUnit "zfs-chown-${utils.escapeSystemdPath guestCfg.zfs.mountpoint}.service"];
|
|
||||||
after = [fsMountUnit "zfs-chown-${utils.escapeSystemdPath guestCfg.zfs.mountpoint}.service"];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Ensure that the zfs dataset has the correct permissions when mounted
|
||||||
|
"zfs-chown-${utils.escapeSystemdPath zfsCfg.hostMountpoint}" = {
|
||||||
|
after = [fsMountUnit];
|
||||||
|
unitConfig.DefaultDependencies = "no";
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
script = ''
|
||||||
|
chmod 700 ${escapeShellArg zfsCfg.hostMountpoint}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
"microvm@${guestName}" = mkIf (guestCfg.backend == "microvm") {
|
||||||
|
requires = [fsMountUnit "zfs-chown-${utils.escapeSystemdPath zfsCfg.hostMountpoint}.service"];
|
||||||
|
after = [fsMountUnit "zfs-chown-${utils.escapeSystemdPath zfsCfg.hostMountpoint}.service"];
|
||||||
|
};
|
||||||
|
|
||||||
|
"container@${guestName}" = mkIf (guestCfg.backend == "container") {
|
||||||
|
requires = [fsMountUnit "zfs-chown-${utils.escapeSystemdPath zfsCfg.hostMountpoint}.service"];
|
||||||
|
after = [fsMountUnit "zfs-chown-${utils.escapeSystemdPath zfsCfg.hostMountpoint}.service"];
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
|
||||||
microvm.vms.${guestName} =
|
microvm.vms.${guestName} =
|
||||||
mkIf (guestCfg.backend == "microvm") (import ./microvm.nix guestName guestCfg attrs);
|
mkIf (guestCfg.backend == "microvm") (import ./microvm.nix guestName guestCfg attrs);
|
||||||
|
|
||||||
|
@ -97,17 +97,10 @@ in {
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
options.node = {
|
options.node.type = mkOption {
|
||||||
type = mkOption {
|
type = types.enum ["host" "microvm" "container"];
|
||||||
type = types.enum ["host" "microvm" "container"];
|
description = "The type of this machine.";
|
||||||
description = "The type of this machine.";
|
default = "host";
|
||||||
default = "host";
|
|
||||||
};
|
|
||||||
|
|
||||||
guestName = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
description = "The base name of this machine without the parent's name. Only defined if this is a guest.";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
options.containers = mkOption {
|
options.containers = mkOption {
|
||||||
|
@ -126,11 +119,11 @@ in {
|
||||||
options.guests = mkOption {
|
options.guests = mkOption {
|
||||||
default = {};
|
default = {};
|
||||||
description = "Defines the actual vms and handles the necessary base setup for them.";
|
description = "Defines the actual vms and handles the necessary base setup for them.";
|
||||||
type = types.attrsOf (types.submodule ({name, ...}: {
|
type = types.attrsOf (types.submodule (submod: {
|
||||||
options = {
|
options = {
|
||||||
nodeName = mkOption {
|
nodeName = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = "${nodeName}-${name}";
|
default = "${nodeName}-${submod.config._module.args.name}";
|
||||||
description = ''
|
description = ''
|
||||||
The name of the resulting node. By default this will be a compound name
|
The name of the resulting node. By default this will be a compound name
|
||||||
of the host's name and the vm's name to avoid name clashes. Can be
|
of the host's name and the vm's name to avoid name clashes. Can be
|
||||||
|
@ -175,25 +168,37 @@ in {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
zfs = {
|
zfs = mkOption {
|
||||||
enable = mkEnableOption "persistent data on separate zfs dataset";
|
description = "zfs datasets to mount into the guest";
|
||||||
|
default = {};
|
||||||
|
type = types.attrsOf (types.submodule (zfsSubmod: {
|
||||||
|
options = {
|
||||||
|
pool = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "The host's zfs pool on which the dataset resides";
|
||||||
|
};
|
||||||
|
|
||||||
pool = mkOption {
|
dataset = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
description = "The host's zfs pool on which the dataset resides";
|
example = "safe/guests/mycontainer";
|
||||||
};
|
description = "The host's dataset that should be used for this mountpoint (will automatically be created, including parent datasets)";
|
||||||
|
};
|
||||||
|
|
||||||
dataset = mkOption {
|
hostMountpoint = mkOption {
|
||||||
type = types.str;
|
type = types.path;
|
||||||
default = "safe/guests/${name}";
|
default = "/guests/${submod.config._module.args.name}${zfsSubmod.config._module.args.name}";
|
||||||
description = "The host's dataset that should be used for this vm's state (will automatically be created, parent dataset must exist)";
|
example = "/guests/mycontainer/persist";
|
||||||
};
|
description = "The host's mountpoint for the guest's dataset";
|
||||||
|
};
|
||||||
|
|
||||||
mountpoint = mkOption {
|
guestMountpoint = mkOption {
|
||||||
type = types.str;
|
type = types.path;
|
||||||
default = "/guests/${name}";
|
default = zfsSubmod.config._module.args.name;
|
||||||
description = "The host's mountpoint for the vm's dataset (will be shared via virtiofs as /persist in the vm)";
|
example = "/persist";
|
||||||
};
|
description = "The mountpoint inside the guest.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
autostart = mkOption {
|
autostart = mkOption {
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
concatStringsSep
|
concatStringsSep
|
||||||
duplicates
|
duplicates
|
||||||
mapAttrsToList
|
mapAttrsToList
|
||||||
mkIf
|
|
||||||
mkOption
|
mkOption
|
||||||
types
|
types
|
||||||
;
|
;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue