Make bcachefs subvolumes boot-time mount tests pass + more tests

This commit is contained in:
nothingnesses
2025-04-26 00:29:40 +01:00
committed by Jörg Thalheim
parent d0c543d740
commit 7b63642358
4 changed files with 167 additions and 176 deletions

3
.gitignore vendored
View File

@@ -4,3 +4,6 @@ result-*
# Created by the NixOS interactive test driver
.nixos-test-history
# Created by `:bl` in `nix repl`
repl-result-out

View File

@@ -23,7 +23,7 @@
content = {
type = "bcachefs";
# This refers to a filesystem in the `bcachefs_filesystems` attrset below.
filesystem = "unmounted_subvolumes_in_multi";
filesystem = "mounted_subvolumes_in_multi";
label = "group_a.vdb2";
extraFormatArgs = [
"--discard"
@@ -44,7 +44,7 @@
size = "100%";
content = {
type = "bcachefs";
filesystem = "unmounted_subvolumes_in_multi";
filesystem = "mounted_subvolumes_in_multi";
label = "group_a.vdc1";
extraFormatArgs = [
"--discard"
@@ -65,7 +65,7 @@
size = "100%";
content = {
type = "bcachefs";
filesystem = "unmounted_subvolumes_in_multi";
filesystem = "mounted_subvolumes_in_multi";
label = "group_b.vdd1";
extraFormatArgs = [
"--force"
@@ -79,87 +79,6 @@
vde = {
device = "/dev/vde";
type = "disk";
content = {
type = "gpt";
partitions = {
vde1 = {
size = "100%";
content = {
type = "bcachefs";
filesystem = "mounted_subvolumes_in_multi";
label = "group_a.vde1";
extraFormatArgs = [
"--discard"
];
};
};
};
};
};
vdf = {
device = "/dev/vdf";
type = "disk";
content = {
type = "gpt";
partitions = {
vdf1 = {
size = "100%";
content = {
type = "bcachefs";
filesystem = "mounted_subvolumes_in_multi";
label = "group_a.vdf1";
extraFormatArgs = [
"--discard"
];
};
};
};
};
};
vdg = {
device = "/dev/vdg";
type = "disk";
content = {
type = "gpt";
partitions = {
vdd1 = {
size = "100%";
content = {
type = "bcachefs";
filesystem = "mounted_subvolumes_in_multi";
label = "group_b.vdg1";
extraFormatArgs = [
"--force"
];
};
};
};
};
};
vdh = {
device = "/dev/vdh";
type = "disk";
content = {
type = "gpt";
partitions = {
vdd1 = {
size = "100%";
content = {
type = "bcachefs";
filesystem = "no_reliance_on_external_subvolume";
label = "group_a.vdh1";
};
};
};
};
};
vdi = {
device = "/dev/vdi";
type = "disk";
content = {
type = "gpt";
partitions = {
@@ -168,7 +87,7 @@
content = {
type = "bcachefs";
filesystem = "relies_on_external_subvolume";
label = "group_a.vdi1";
label = "group_a.vde1";
};
};
};
@@ -177,72 +96,52 @@
};
bcachefs_filesystems = {
# Example showing unmounted subvolumes in a multi-disk configuration.
unmounted_subvolumes_in_multi = {
# Example showing mounted subvolumes in a multi-disk configuration.
mounted_subvolumes_in_multi = {
type = "bcachefs_filesystem";
passwordFile = "/tmp/secret.key";
extraFormatArgs = [
"--compression=lz4"
"--background_compression=lz4"
];
subvolumes = {
# Subvolume name is different from mountpoint.
"subvolumes/root" = {
mountpoint = "/";
mountOptions = [
"verbose"
];
};
# Subvolume name is the same as the mountpoint.
"subvolumes/home" = {
mountpoint = "/home";
};
# Nested subvolume doesn't need a mountpoint as its parent is mounted.
"subvolumes/home/user" = {
};
# Parent is not mounted so the mountpoint must be set.
"subvolumes/nix" = {
mountpoint = "/nix";
};
# This subvolume will be created but not mounted.
"subvolumes/test" = {
};
};
};
# Example showing a bcachefs filesystem without subvolumes
# and which relies on a subvolume in another filesystem being mounted.
relies_on_external_subvolume = {
type = "bcachefs_filesystem";
mountpoint = "/home/Documents";
extraFormatArgs = [
"--compression=lz4"
"--background_compression=lz4"
];
mountOptions = [
"verbose"
];
mountpoint = "/";
subvolumes = {
"subvolumes/rootfs" = { };
"subvolumes/home" = { };
"subvolumes/home/user" = { };
"subvolumes/nix" = { };
"subvolumes/test" = { };
};
};
# # Example showing mounted subvolumes in a multi-disk configuration (not yet working).
# mounted_subvolumes_in_multi = {
# type = "bcachefs_filesystem";
# passwordFile = "/tmp/secret.key";
# extraFormatArgs = [
# "--compression=lz4"
# "--background_compression=lz4"
# ];
# mountOptions = [
# "verbose"
# ];
# subvolumes = {
# # Subvolume name is different from mountpoint
# "foo" = {
# mountpoint = "/bar";
# };
# # Subvolume name is the same as the mountpoint
# "home" = {
# mountpoint = "/home";
# };
# # Sub(sub)volume doesn't need a mountpoint as its parent is mounted
# "home/user" = {
# };
# # Parent is not mounted so the mountpoint must be set
# "nix" = {
# mountpoint = "/nix";
# };
# # This subvolume will be created but not mounted
# "test" = {
# };
# };
# };
# Example showing another bcachefs filesystem.
no_reliance_on_external_subvolume = {
type = "bcachefs_filesystem";
mountpoint = "/sometestdir";
};
# # Example showing another bcachefs filesystem that relies on a subvolume
# # in another filesystem being mounted (not yet working).
# relies_on_external_subvolume = {
# type = "bcachefs_filesystem";
# mountpoint = "/home/somedir/vdf1";
# };
};
};
}

View File

@@ -113,10 +113,7 @@
mountpoint = lib.mkOption {
type = lib.types.nullOr diskoLib.optionTypes.absolute-pathname;
default = null;
description = ''
Path to mount the subvolume to.
DO NOT USE. Currently not working.
'';
description = "Path to mount the subvolume to.";
example = "/";
};
};
@@ -126,7 +123,19 @@
default = { };
description = "List of subvolumes to define.";
example = {
"subvolumes/home" = { };
"subvolumes/root" = {
mountpoint = "/";
extraFormatArgs = [
"--compression=lz4"
"--background_compression=lz4"
];
mountOptions = [
"verbose"
];
};
"subvolumes/home" = {
mountpoint = "/home";
};
};
};
_parent = lib.mkOption {
@@ -291,7 +300,6 @@
_config = lib.mkOption {
internal = true;
readOnly = true;
# @todo Check that this implementation is correct:
default =
(lib.optional (config.mountpoint != null) {
fileSystems.${config.mountpoint} = {

View File

@@ -7,39 +7,120 @@ diskoLib.testLib.makeDiskoTest {
name = "bcachefs";
disko-config = ../example/bcachefs.nix;
enableOCR = true;
bootCommands = ''
machine.wait_for_text("enter passphrase for /");
machine.send_chars("secretsecret\n");
machine.wait_for_text("enter passphrase for /home");
machine.send_chars("secretsecret\n");
machine.wait_for_text("enter passphrase for /nix");
machine.send_chars("secretsecret\n");
'';
extraSystemConfig = {
environment.systemPackages = [
pkgs.jq
];
};
extraTestScript = ''
machine.succeed("mountpoint /");
# @todo Verify all devices are part of the filesystem.
# @todo Check device labels and group assignments.
# Verify mount options were applied.
machine.succeed("mount | grep ' / ' | grep -q 'compression=lz4'");
machine.succeed("mount | grep ' / ' | grep -q 'background_compression=lz4'");
# @todo Verify mountpoint dependency order was respected.
# @todo Add tests for subvolumes.
# Print debug information.
machine.succeed("ls -la /subvolumes >&2");
machine.succeed("lsblk >&2");
machine.succeed("lsblk -f >&2");
machine.succeed("mount >&2");
'';
# extraSystemConfig = { pkgs, ... }: {
# # @todo Do we need to add any attributes here?
# boot = {
# supportedFilesystems = [ "bcachefs" ];
# initrd = {
# supportedFilesystems = [ "bcachefs" ];
# # systemd.enable = false;
# };
# };
# environment.systemPackages = [
# pkgs.bcachefs-tools
# pkgs.util-linux
# ];
# };
# extraInstallerConfig = {
# # @todo Do we need to add any attributes here?
# };
bootCommands = ''
machine.wait_for_text("enter passphrase for");
machine.send_chars("secretsecret\n");
machine.succeed("bcachefs show-super /dev/vda2 >&2");
machine.succeed("bcachefs show-super /dev/vdd1 >&2");
machine.succeed("findmnt --json >&2");
# Verify subvolume structure.
machine.succeed("test -d /subvolumes/root");
machine.succeed("test -d /subvolumes/home");
machine.succeed("test -d /subvolumes/home/user");
machine.succeed("test -d /subvolumes/nix");
machine.succeed("test -d /subvolumes/test");
machine.fail("test -d /subvolumes/non-existent");
# Verify existence of mountpoints.
machine.succeed("mountpoint /");
machine.succeed("mountpoint /home");
machine.succeed("mountpoint /nix");
machine.succeed("mountpoint /home/Documents");
machine.fail("mountpoint /non-existent");
# Verify device membership and labels.
machine.succeed("bcachefs show-super /dev/vda2 | grep 'Devices:' | grep -q '3'");
machine.succeed("bcachefs show-super /dev/vdd1 | grep 'Devices:' | grep -q '1'");
machine.succeed("bcachefs show-super /dev/vda2 | grep 'Label:' | grep -q 'vdb2'");
machine.succeed("bcachefs show-super /dev/vda2 | grep 'Label:' | grep -q 'vdc1'");
machine.succeed("bcachefs show-super /dev/vda2 | grep 'Label:' | grep -q 'vdd1'");
machine.succeed("bcachefs show-super /dev/vdd1 | grep 'Label:' | grep -q 'vde1'");
machine.fail("bcachefs show-super /dev/vda2 | grep 'Label:' | grep -q 'non-existent'");
# @todo Verify format arguments.
# Verify mount options from configuration.
machine.succeed("""
findmnt --json \
| jq -e ' \
.filesystems[] \
| select(.target == "/") \
| .options \
| split(",") \
| contains(["verbose", "compression=lz4", "background_compression=lz4"]) \
'
""");
machine.succeed("""
findmnt --json \
| jq -e ' \
.filesystems[] \
| .. \
| select(.target? == "/home/Documents") \
| .options \
| split(",") \
| contains(["verbose"]) \
'
""");
machine.fail("""
findmnt --json \
| jq -e ' \
.filesystems[] \
| select(.target == "/") \
| .options \
| split(",") \
| contains(["non-existent"]) \
'
""");
# Verify device composition of filesystems.
machine.succeed("""
findmnt --json \
| jq -e ' \
.filesystems[] \
| select(.target == "/") \
| .source | split(":") \
| contains(["/dev/vda2", "/dev/vdb1", "/dev/vdc1"]) \
'
""");
machine.succeed("""
findmnt --json \
| jq -e ' \
.filesystems[] \
| .. \
| select(.target? == "/home/Documents") \
| .source \
| contains("/dev/vdd1") \
'
""");
machine.fail("""
findmnt --json \
| jq -e ' \
.filesystems[] \
| select(.target == "/") \
| .source | split(":") \
| contains(["/dev/non-existent"]) \
'
""");
'';
}