feat: made impermanence create datasets for zfs and persistence
This commit is contained in:
parent
409fdb7276
commit
adc6b90c93
4 changed files with 188 additions and 43 deletions
|
|
@ -1,33 +1,90 @@
|
||||||
args @ {lib, ...}: let
|
args @ {
|
||||||
impermanenceDatasetSubmodules = (import ./submodules/impermanenceDataset.nix) args;
|
lib,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
datasetSubmodules = (import ./submodules/dataset.nix) args;
|
||||||
|
impermanenceDatasetSubmodule = (import ./submodules/impermanenceDataset.nix) args;
|
||||||
|
|
||||||
|
permissionsToMode = permissions: let
|
||||||
|
permSetToDigit = permSet:
|
||||||
|
(
|
||||||
|
if permSet.read
|
||||||
|
then 4
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
+ (
|
||||||
|
if permSet.write
|
||||||
|
then 2
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
+ (
|
||||||
|
if permSet.execute
|
||||||
|
then 1
|
||||||
|
else 0
|
||||||
|
);
|
||||||
|
|
||||||
|
ownerDigit = permSetToDigit permissions.owner.permissions;
|
||||||
|
groupDigit = permSetToDigit permissions.group.permissions;
|
||||||
|
otherDigit = permSetToDigit permissions.other.permissions;
|
||||||
|
in
|
||||||
|
toString ownerDigit + toString groupDigit + toString otherDigit;
|
||||||
|
|
||||||
|
# Get the option names from both submodules to automatically determine which are impermanence-specific
|
||||||
|
regularDatasetEval = lib.evalModules {
|
||||||
|
modules = [datasetSubmodules];
|
||||||
|
specialArgs = args;
|
||||||
|
};
|
||||||
|
impermanenceDatasetEval = lib.evalModules {
|
||||||
|
modules = [impermanenceDatasetSubmodule];
|
||||||
|
specialArgs = args;
|
||||||
|
};
|
||||||
|
|
||||||
|
regularDatasetOptions = builtins.attrNames regularDatasetEval.options;
|
||||||
|
impermanenceDatasetOptions = builtins.attrNames impermanenceDatasetEval.options;
|
||||||
|
|
||||||
|
# Find options that are only in impermanence datasets (not in regular ZFS datasets)
|
||||||
|
impermanenceOnlyOptions = lib.lists.subtractLists regularDatasetOptions impermanenceDatasetOptions;
|
||||||
in {
|
in {
|
||||||
options.storage = {
|
options.storage = {
|
||||||
impermanence = {
|
impermanence = {
|
||||||
enable = lib.mkEnableOption "should impermanence be enabled for this system";
|
enable = lib.mkEnableOption "should impermanence be enabled for this system";
|
||||||
# TODO: enable option implementation
|
|
||||||
|
|
||||||
# TODO: assertion that zfs needs to be enabled when impermanence is enabled
|
|
||||||
|
|
||||||
# TODO: datasets option that is a submodule that will be used to define what datasets to add to the storage system
|
|
||||||
# We should by default create the `local`, `local/system/nix`, `local/system/root`, `persist` `persist/system/root`, and `persist/system/var/log` datasets
|
|
||||||
# We should also create datasets for systemd modules that have have impermanence enabled for them
|
|
||||||
|
|
||||||
datasets = lib.mkOption {
|
datasets = lib.mkOption {
|
||||||
type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodules);
|
type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodule);
|
||||||
default = {};
|
default = {};
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# TODO: this should just live under home-manager.users.<user>.storage.impermanence
|
config = lib.mkIf config.storage.impermanence.enable (lib.mkMerge [
|
||||||
home-manager = lib.mkOption {
|
{
|
||||||
type = lib.types.attrsOf (lib.types.submodule ({name, ...}: {
|
environment.persistence =
|
||||||
enable = lib.mkEnableOption "should impermanence be enabled for this user";
|
lib.mapAttrs (datasetName: dataset: {
|
||||||
# We should by default create the `local/home/${name}`, and `persist/home/${name}` datasets
|
enable = true;
|
||||||
datasets = lib.mkOption {
|
hideMounts = true;
|
||||||
type = lib.types.attrsOf (lib.types.submodule impermanenceDatasetSubmodules);
|
directories = lib.mapAttrsToList (path: dirConfig: {
|
||||||
default = {};
|
directory = path;
|
||||||
};
|
user = dirConfig.owner.name;
|
||||||
}));
|
group = dirConfig.group.name;
|
||||||
};
|
mode = permissionsToMode dirConfig;
|
||||||
};
|
}) (lib.filterAttrs (_: dirConfig: dirConfig.enable) dataset.directories);
|
||||||
};
|
files = lib.mapAttrsToList (path: fileConfig: {
|
||||||
|
file = path;
|
||||||
|
user = fileConfig.owner.name;
|
||||||
|
group = fileConfig.group.name;
|
||||||
|
mode = permissionsToMode fileConfig;
|
||||||
|
}) (lib.filterAttrs (_: fileConfig: fileConfig.enable) dataset.files);
|
||||||
|
})
|
||||||
|
config.storage.impermanence.datasets;
|
||||||
|
}
|
||||||
|
(lib.mkIf config.storage.zfs.enable {
|
||||||
|
storage.zfs.datasets =
|
||||||
|
lib.mapAttrs (
|
||||||
|
datasetName: dataset:
|
||||||
|
builtins.removeAttrs dataset impermanenceOnlyOptions
|
||||||
|
)
|
||||||
|
config.storage.impermanence.datasets;
|
||||||
|
})
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
73
modules/nixos-modules/storage/storage.nix
Normal file
73
modules/nixos-modules/storage/storage.nix
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
util,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
# TODO: create all of the datasets from option and home-manager datasets
|
||||||
|
# TODO: set up datasets for systemd services that want a dataset created
|
||||||
|
config = lib.mkMerge [
|
||||||
|
(
|
||||||
|
lib.mkIf config.storage.zfs.enable (lib.mkMerge [
|
||||||
|
{
|
||||||
|
storage.zfs.datasets = {
|
||||||
|
"persist/system/nix" = {
|
||||||
|
type = "zfs_fs";
|
||||||
|
mountpoint = "/nix";
|
||||||
|
options = {
|
||||||
|
atime = "off";
|
||||||
|
relatime = "off";
|
||||||
|
canmount = "on";
|
||||||
|
"com.sun:auto-snapshot" = "false";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"persist/system/var/log" = {
|
||||||
|
type = "zfs_fs";
|
||||||
|
mountpoint = "/persist/system/var/log";
|
||||||
|
options = {
|
||||||
|
"com.sun:auto-snapshot" = "false";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
(util.mkUnless config.storage.impermanence.enable {
|
||||||
|
# TODO: configure datasets for normal zfs
|
||||||
|
# TODO: create datasets for systemd.services.<name>.storage.impermanence.datasets
|
||||||
|
storage.zfs.datasets = {
|
||||||
|
"persist/system/root" = {
|
||||||
|
type = "zfs_fs";
|
||||||
|
mountpoint = "/";
|
||||||
|
canmount = "on";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
(lib.mkIf config.storage.impermanence.enable {
|
||||||
|
storage.impermanence.datasets = {
|
||||||
|
"persist/system/root" = {
|
||||||
|
type = "zfs_fs";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
storage.zfs.datasets = {
|
||||||
|
# TODO: is there a way that we can link these two folders in configs via storage.impermanence.datasets
|
||||||
|
"local/system/root" = {
|
||||||
|
type = "zfs_fs";
|
||||||
|
mountpoint = "/";
|
||||||
|
options = {
|
||||||
|
canmount = "on";
|
||||||
|
};
|
||||||
|
postCreateHook = ''
|
||||||
|
zfs snapshot rpool/local/system/root@blank
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# TODO: home-manager.users.<user>.storage.impermanence.enable
|
||||||
|
# is false then persist the entire directory of the user
|
||||||
|
# if true persist home-manager.users.<user>.storage.impermanence.datasets
|
||||||
|
# TODO: systemd.services.<name>.storage.datasets persists
|
||||||
|
})
|
||||||
|
])
|
||||||
|
)
|
||||||
|
# TODO: configure other needed storage modes here
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,10 @@
|
||||||
{lib, ...}: {...}: let
|
args @ {
|
||||||
|
lib,
|
||||||
|
name,
|
||||||
|
...
|
||||||
|
}: {...}: let
|
||||||
|
datasetSubmodule = (import ./dataset.nix) args;
|
||||||
|
|
||||||
pathPermissions = {
|
pathPermissions = {
|
||||||
read = lib.mkEnableOption "should the path have read permissions";
|
read = lib.mkEnableOption "should the path have read permissions";
|
||||||
write = lib.mkEnableOption "should the path have read permissions";
|
write = lib.mkEnableOption "should the path have read permissions";
|
||||||
|
|
@ -11,14 +17,14 @@
|
||||||
default = true;
|
default = true;
|
||||||
};
|
};
|
||||||
owner = {
|
owner = {
|
||||||
user = lib.mkOption {
|
name = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = "nouser";
|
default = "nouser";
|
||||||
};
|
};
|
||||||
permissions = pathPermissions;
|
permissions = pathPermissions;
|
||||||
};
|
};
|
||||||
group = {
|
group = {
|
||||||
group = lib.mkOption {
|
name = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = "nogroup";
|
default = "nogroup";
|
||||||
};
|
};
|
||||||
|
|
@ -31,16 +37,21 @@
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
imports = [
|
imports = [
|
||||||
./dataset.nix
|
datasetSubmodule
|
||||||
];
|
];
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
files = lib.types.mkOption {
|
files = lib.mkOption {
|
||||||
type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule);
|
type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule);
|
||||||
default = {};
|
default = {};
|
||||||
};
|
};
|
||||||
directories = {
|
directories = lib.mkOption {
|
||||||
type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule);
|
type = lib.types.attrsOf (lib.types.submodule pathTypeSubmodule);
|
||||||
default = {};
|
default = {};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
mountpoint = "/${name}";
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,10 @@ args @ {
|
||||||
# max gpt length is 36 and disk adds formats it like disk-xxxx-zfs which means we need to be 9 characters under that
|
# max gpt length is 36 and disk adds formats it like disk-xxxx-zfs which means we need to be 9 characters under that
|
||||||
hashDisk = drive: (builtins.substring 0 27 (builtins.hashString "sha256" drive));
|
hashDisk = drive: (builtins.substring 0 27 (builtins.hashString "sha256" drive));
|
||||||
|
|
||||||
poolVdevs = [
|
poolVdevs =
|
||||||
(builtins.map (
|
builtins.map (
|
||||||
|
vdev:
|
||||||
|
builtins.map (
|
||||||
device: let
|
device: let
|
||||||
deviceStr =
|
deviceStr =
|
||||||
if builtins.isString device
|
if builtins.isString device
|
||||||
|
|
@ -19,8 +21,9 @@ args @ {
|
||||||
in
|
in
|
||||||
lib.attrsets.nameValuePair (hashDisk deviceStr) deviceStr
|
lib.attrsets.nameValuePair (hashDisk deviceStr) deviceStr
|
||||||
)
|
)
|
||||||
config.storage.zfs.pool.vdevs)
|
vdev
|
||||||
];
|
)
|
||||||
|
config.storage.zfs.pool.vdevs;
|
||||||
|
|
||||||
poolCache = builtins.map (
|
poolCache = builtins.map (
|
||||||
name: let
|
name: let
|
||||||
|
|
@ -45,7 +48,7 @@ args @ {
|
||||||
then false
|
then false
|
||||||
else device.boot
|
else device.boot
|
||||||
)
|
)
|
||||||
config.storage.zfs.pool.vdevs);
|
(lib.lists.flatten config.storage.zfs.pool.vdevs));
|
||||||
|
|
||||||
allDrives = (lib.lists.flatten poolVdevs) ++ poolCache;
|
allDrives = (lib.lists.flatten poolVdevs) ++ poolCache;
|
||||||
in {
|
in {
|
||||||
|
|
@ -113,8 +116,9 @@ in {
|
||||||
description = "Size of the boot partition on boot drives";
|
description = "Size of the boot partition on boot drives";
|
||||||
};
|
};
|
||||||
vdevs = lib.mkOption {
|
vdevs = lib.mkOption {
|
||||||
type = lib.types.listOf deviceType;
|
type = lib.types.listOf (lib.types.listOf deviceType);
|
||||||
default = [];
|
default = [];
|
||||||
|
description = "List of vdevs, where each vdev is a list of devices";
|
||||||
};
|
};
|
||||||
cache = lib.mkOption {
|
cache = lib.mkOption {
|
||||||
type = lib.types.attrsOf deviceType;
|
type = lib.types.attrsOf deviceType;
|
||||||
|
|
@ -359,7 +363,7 @@ in {
|
||||||
fi
|
fi
|
||||||
''
|
''
|
||||||
)
|
)
|
||||||
config.storage.zfs.pool.vdevs}
|
(lib.lists.flatten config.storage.zfs.pool.vdevs)}
|
||||||
|
|
||||||
# Check pool mode matches configuration
|
# Check pool mode matches configuration
|
||||||
if ! echo "$pool_status" | grep -q "$expected_mode"; then
|
if ! echo "$pool_status" | grep -q "$expected_mode"; then
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue