119 lines
3.7 KiB
Nix
119 lines
3.7 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
utils,
|
|
pkgs,
|
|
...
|
|
}: let
|
|
inherit
|
|
(lib)
|
|
mapAttrs'
|
|
concatStrings
|
|
nameValuePair
|
|
mapAttrsToList
|
|
flip
|
|
types
|
|
mkOption
|
|
mkEnableOption
|
|
mdDoc
|
|
mkIf
|
|
disko
|
|
makeBinPath
|
|
escapeShellArg
|
|
mkMerge
|
|
;
|
|
in {
|
|
options.containers = mkOption {
|
|
type = types.attrsOf (types.submodule (
|
|
{name, ...}: {
|
|
options = {
|
|
zfs = {
|
|
enable = mkEnableOption (mdDoc "persistent data on separate zfs dataset");
|
|
|
|
pool = mkOption {
|
|
type = types.str;
|
|
description = mdDoc "The host's zfs pool on which the dataset resides";
|
|
};
|
|
|
|
dataset = mkOption {
|
|
type = types.str;
|
|
default = "safe/containers/${name}";
|
|
description = mdDoc "The host's dataset that should be used for this containers persistent data (will automatically be created, parent dataset must exist)";
|
|
};
|
|
|
|
mountpoint = mkOption {
|
|
type = types.str;
|
|
description = mdDoc "The host's mountpoint for the containers dataset";
|
|
};
|
|
};
|
|
};
|
|
}
|
|
));
|
|
};
|
|
config.system.activationScripts = let
|
|
mkDir = paths: (concatStrings (flip map paths (path: ''
|
|
[[ -d "${path}" ]] || ${pkgs.coreutils}/bin/mkdir -p "${path}"
|
|
'')));
|
|
in
|
|
flip mapAttrs' config.containers (
|
|
name: value:
|
|
nameValuePair "mkContainerFolder-${name}" (mkDir (mapAttrsToList (_: x: x.hostPath) value.bindMounts))
|
|
);
|
|
config.disko = mkMerge (flip mapAttrsToList config.containers
|
|
(
|
|
_: cfg: {
|
|
devices.zpool = mkIf cfg.zfs.enable {
|
|
${cfg.zfs.pool}.datasets."${cfg.zfs.dataset}" =
|
|
disko.zfs.filesystem cfg.zfs.mountpoint;
|
|
};
|
|
|
|
# Ensure that the zfs dataset exists before it is mounted.
|
|
}
|
|
));
|
|
config.systemd = mkMerge (flip mapAttrsToList config.containers
|
|
(
|
|
name: cfg: {
|
|
services = let
|
|
fsMountUnit = "${utils.escapeSystemdPath cfg.zfs.mountpoint}.mount";
|
|
in
|
|
mkIf cfg.zfs.enable {
|
|
# Ensure that the zfs dataset exists before it is mounted.
|
|
"zfs-ensure-${utils.escapeSystemdPath cfg.zfs.mountpoint}" = {
|
|
wantedBy = [fsMountUnit];
|
|
before = [fsMountUnit];
|
|
after = [
|
|
"zfs-import-${utils.escapeSystemdPath cfg.zfs.pool}.service"
|
|
"zfs-mount.target"
|
|
];
|
|
unitConfig.DefaultDependencies = "no";
|
|
serviceConfig.Type = "oneshot";
|
|
script = let
|
|
poolDataset = "${cfg.zfs.pool}/${cfg.zfs.dataset}";
|
|
diskoDataset = config.disko.devices.zpool.${cfg.zfs.pool}.datasets.${cfg.zfs.dataset};
|
|
in ''
|
|
export PATH=${makeBinPath [pkgs.zfs]}":$PATH"
|
|
if ! zfs list -H -o type ${escapeShellArg poolDataset} &>/dev/null ; then
|
|
${diskoDataset._create}
|
|
fi
|
|
'';
|
|
};
|
|
|
|
# Ensure that the zfs dataset has the correct permissions when mounted
|
|
"zfs-chown-${utils.escapeSystemdPath cfg.zfs.mountpoint}" = {
|
|
after = [fsMountUnit];
|
|
unitConfig.DefaultDependencies = "no";
|
|
serviceConfig.Type = "oneshot";
|
|
script = ''
|
|
chmod 700 ${escapeShellArg cfg.zfs.mountpoint}
|
|
'';
|
|
};
|
|
|
|
"container@${name}" = {
|
|
requires = [fsMountUnit "zfs-chown-${utils.escapeSystemdPath cfg.zfs.mountpoint}.service"];
|
|
after = [fsMountUnit "zfs-chown-${utils.escapeSystemdPath cfg.zfs.mountpoint}.service"];
|
|
};
|
|
};
|
|
}
|
|
));
|
|
}
|