Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/systemd-repart: enable creating root partition #232533

Merged
merged 1 commit into from
May 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 58 additions & 32 deletions nixos/modules/system/boot/systemd/repart.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{ config, pkgs, lib, ... }:
{ config, pkgs, lib, utils, ... }:

let
cfg = config.systemd.repart;
Expand Down Expand Up @@ -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.

To run systemd-repart after the initrd, see
`options.systemd.repart.enable`.
'';
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`.
'';
};

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 = {
Expand Down Expand Up @@ -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 {
Expand Down
62 changes: 60 additions & 2 deletions nixos/tests/systemd-repart.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
'';
};
}