nixos/systemd-repart: enable creating root partition

This commit is contained in:
nikstur 2023-05-18 03:16:35 +02:00
parent a8eea70935
commit ef80e11032
2 changed files with 117 additions and 33 deletions

View file

@ -1,4 +1,4 @@
{ config, pkgs, lib, ... }:
{ config, pkgs, lib, utils, ... }:
let
cfg = config.systemd.repart;
@ -26,14 +26,29 @@ let
in
{
options = {
boot.initrd.systemd.repart.enable = lib.mkEnableOption (lib.mdDoc "systemd-repart") // {
description = lib.mdDoc ''
Grow and add partitions to a partition table at boot time in the initrd.
systemd-repart only works with GPT partition tables.
boot.initrd.systemd.repart = {
enable = lib.mkEnableOption (lib.mdDoc "systemd-repart") // {
description = lib.mdDoc ''
Grow and add partitions to a partition table at boot time in the initrd.
systemd-repart only works with GPT partition tables.
To run systemd-repart after the initrd, see
`options.systemd.repart.enable`.
'';
To run systemd-repart after the initrd, see
`options.systemd.repart.enable`.
'';
};
device = lib.mkOption {
type = with lib.types; nullOr str;
description = lib.mdDoc ''
The device to operate on.
If `device == null`, systemd-repart will operate on the device
backing the root partition. So in order to dynamically *create* the
root partition in the initrd you need to set a device.
'';
default = null;
example = "/dev/vda";
};
};
systemd.repart = {
@ -84,31 +99,42 @@ in
contents."/etc/repart.d".source = definitionsDirectory;
# Override defaults in upstream unit.
services.systemd-repart = {
# systemd-repart tries to create directories in /var/tmp by default to
# store large temporary files that benefit from persistence on disk. In
# the initrd, however, /var/tmp does not provide more persistence than
# /tmp, so we re-use it here.
environment."TMPDIR" = "/tmp";
serviceConfig = {
ExecStart = [
" " # required to unset the previous value.
# When running in the initrd, systemd-repart by default searches
# for definition files in /sysroot or /sysusr. We tell it to look
# in the initrd itself.
''${config.boot.initrd.systemd.package}/bin/systemd-repart \
services.systemd-repart =
let
deviceUnit = "${utils.escapeSystemdPath initrdCfg.device}.device";
in
{
# systemd-repart tries to create directories in /var/tmp by default to
# store large temporary files that benefit from persistence on disk. In
# the initrd, however, /var/tmp does not provide more persistence than
# /tmp, so we re-use it here.
environment."TMPDIR" = "/tmp";
serviceConfig = {
ExecStart = [
" " # required to unset the previous value.
# When running in the initrd, systemd-repart by default searches
# for definition files in /sysroot or /sysusr. We tell it to look
# in the initrd itself.
''${config.boot.initrd.systemd.package}/bin/systemd-repart \
--definitions=/etc/repart.d \
--dry-run=no
''
];
--dry-run=no ${lib.optionalString (initrdCfg.device != null) initrdCfg.device}
''
];
};
# systemd-repart needs to run after /sysroot (or /sysuser, but we
# don't have it) has been mounted because otherwise it cannot
# determine the device (i.e disk) to operate on. If you want to run
# systemd-repart without /sysroot (i.e. to create the root
# partition), you have to explicitly tell it which device to operate
# on. The service then needs to be ordered to run after this device
# is available.
requires = lib.mkIf (initrdCfg.device != null) [ deviceUnit ];
after =
if initrdCfg.device == null then
[ "sysroot.mount" ]
else
[ deviceUnit ];
};
# systemd-repart needs to run after /sysroot (or /sysuser, but we don't
# have it) has been mounted because otherwise it cannot determine the
# device (i.e disk) to operate on. If you want to run systemd-repart
# without /sysroot, you have to explicitly tell it which device to
# operate on.
after = [ "sysroot.mount" ];
};
};
environment.etc = lib.mkIf cfg.enable {

View file

@ -56,8 +56,8 @@ let
# however, creates separate filesystem images without a partition table, so
# we have to create a disk image manually.
#
# This creates two partitions, an ESP mounted on /dev/vda1 and the root
# partition mounted on /dev/vda2
# This creates two partitions, an ESP available as /dev/vda1 and the root
# partition available as /dev/vda2.
system.build.diskImage = import ../lib/make-disk-image.nix {
inherit config pkgs lib;
# Use a raw format disk so that it can be resized before starting the
@ -131,4 +131,62 @@ in
assert "Growing existing partition 1." in systemd_repart_logs
'';
};
create-root = makeTest {
name = "systemd-repart-create-root";
meta.maintainers = with maintainers; [ nikstur ];
nodes.machine = { config, lib, pkgs, ... }: {
virtualisation.useDefaultFilesystems = false;
virtualisation.fileSystems = {
"/" = {
device = "/dev/disk/by-partlabel/created-root";
fsType = "ext4";
};
"/nix/store" = {
device = "/dev/vda2";
fsType = "ext4";
};
};
# Create an image containing only the Nix store. This enables creating
# the root partition with systemd-repart and then successfully booting
# into a working system.
#
# This creates two partitions, an ESP available as /dev/vda1 and the Nix
# store available as /dev/vda2.
system.build.diskImage = import ../lib/make-disk-image.nix {
inherit config pkgs lib;
onlyNixStore = true;
format = "raw";
bootSize = "32M";
additionalSpace = "0M";
partitionTableType = "efi";
installBootLoader = false;
copyChannel = false;
};
boot.initrd.systemd.enable = true;
boot.initrd.systemd.repart.enable = true;
boot.initrd.systemd.repart.device = "/dev/vda";
systemd.repart.partitions = {
"10-root" = {
Type = "root";
Label = "created-root";
Format = "ext4";
};
};
};
testScript = { nodes, ... }: ''
${useDiskImage nodes.machine}
machine.start()
machine.wait_for_unit("multi-user.target")
systemd_repart_logs = machine.succeed("journalctl --boot --unit systemd-repart.service")
assert "Adding new partition 2 to partition table." in systemd_repart_logs
'';
};
}