From 80a578580fae24c59611091e45f4d04e7d626a2d Mon Sep 17 00:00:00 2001 From: Arian van Putten Date: Mon, 16 Oct 2023 11:57:34 +0000 Subject: [PATCH 1/2] nixos/grow-partition: Resize partition online instead of in initrd There's no reason to do this in initrd. Partitions can be resized online. We just have to make sure it happens before we resize the file system. This also makes grow-partition work with systemd-initrd --- nixos/modules/system/boot/grow-partition.nix | 52 +++++++++----------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/nixos/modules/system/boot/grow-partition.nix b/nixos/modules/system/boot/grow-partition.nix index a2764187a533..1ce4d5e56233 100644 --- a/nixos/modules/system/boot/grow-partition.nix +++ b/nixos/modules/system/boot/grow-partition.nix @@ -16,29 +16,28 @@ with lib; }; config = mkIf config.boot.growPartition { + assertions = [ + { + assertion = !config.boot.initrd.systemd.repart.enable && !config.systemd.repart.enable; + message = "systemd-repart already grows the root partition and thus you should not use boot.growPartition"; + } + ]; + systemd.services.growpart = { + wantedBy = [ "-.mount" ]; + after = [ "-.mount" ]; + before = [ "systemd-growfs-root.service" ]; + conflicts = [ "shutdown.target" ]; + unitConfig.DefaultDependencies = false; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + TimeoutSec = "infinity"; + # growpart returns 1 if the partition is already grown + SuccessExitStatus = "0 1"; + }; - assertions = [{ - assertion = !config.boot.initrd.systemd.enable; - message = "systemd stage 1 does not support 'boot.growPartition' yet."; - }]; - - boot.initrd.extraUtilsCommands = '' - copy_bin_and_libs ${pkgs.gawk}/bin/gawk - copy_bin_and_libs ${pkgs.gnused}/bin/sed - copy_bin_and_libs ${pkgs.util-linux}/sbin/sfdisk - copy_bin_and_libs ${pkgs.util-linux}/sbin/lsblk - - substitute "${pkgs.cloud-utils.guest}/bin/.growpart-wrapped" "$out/bin/growpart" \ - --replace "${pkgs.bash}/bin/sh" "/bin/sh" \ - --replace "awk" "gawk" \ - --replace "sed" "gnused" - - ln -s sed $out/bin/gnused - ''; - - boot.initrd.postDeviceCommands = '' - rootDevice="${config.fileSystems."/".device}" - if waitDevice "$rootDevice"; then + script = '' + rootDevice="${config.fileSystems."/".device}" rootDevice="$(readlink -f "$rootDevice")" parentDevice="$rootDevice" while [ "''${parentDevice%[0-9]}" != "''${parentDevice}" ]; do @@ -48,11 +47,8 @@ with lib; if [ "''${parentDevice%[0-9]p}" != "''${parentDevice}" ] && [ -b "''${parentDevice%p}" ]; then parentDevice="''${parentDevice%p}" fi - TMPDIR=/run sh $(type -P growpart) "$parentDevice" "$partNum" - udevadm settle - fi - ''; - + "${pkgs.cloud-utils.guest}/bin/growpart" "$parentDevice" "$partNum" + ''; + }; }; - } From b756441de221c4c49a9d0559fd0b2681274e3203 Mon Sep 17 00:00:00 2001 From: Arian van Putten Date: Mon, 16 Oct 2023 16:58:19 +0000 Subject: [PATCH 2/2] nixos/grow-partition: add nixos test --- nixos/tests/all-tests.nix | 1 + nixos/tests/grow-partition.nix | 83 ++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 nixos/tests/grow-partition.nix diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 2d774bc19579..ebf784fee58b 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -331,6 +331,7 @@ in { graphite = handleTest ./graphite.nix {}; graylog = handleTest ./graylog.nix {}; grocy = handleTest ./grocy.nix {}; + grow-partition = runTest ./grow-partition.nix; grub = handleTest ./grub.nix {}; guacamole-server = handleTest ./guacamole-server.nix {}; gvisor = handleTest ./gvisor.nix {}; diff --git a/nixos/tests/grow-partition.nix b/nixos/tests/grow-partition.nix new file mode 100644 index 000000000000..344910848dca --- /dev/null +++ b/nixos/tests/grow-partition.nix @@ -0,0 +1,83 @@ +{ lib, ... }: + +let + rootFslabel = "external"; + rootFsDevice = "/dev/disk/by-label/${rootFslabel}"; + + externalModule = partitionTableType: { config, lib, pkgs, ... }: { + virtualisation.directBoot.enable = false; + virtualisation.mountHostNixStore = false; + virtualisation.useEFIBoot = partitionTableType == "efi"; + + # This stops the qemu-vm module from overriding the fileSystems option + # with virtualisation.fileSystems. + virtualisation.fileSystems = lib.mkForce { }; + + + boot.loader.grub.enable = true; + boot.loader.grub.efiSupport = partitionTableType == "efi"; + boot.loader.grub.efiInstallAsRemovable = partitionTableType == "efi"; + boot.loader.grub.device = if partitionTableType == "efi" then "nodev" else "/dev/vda"; + + boot.growPartition = true; + + fileSystems = { + "/".device = rootFsDevice; + }; + + system.build.diskImage = import ../lib/make-disk-image.nix { + inherit config lib pkgs; + label = rootFslabel; + inherit partitionTableType; + format = "raw"; + bootSize = "128M"; + additionalSpace = "0M"; + copyChannel = false; + }; + }; +in +{ + name = "grow-partition"; + + meta.maintainers = with lib.maintainers; [ arianvp ]; + + nodes = { + efi = externalModule "efi"; + legacy = externalModule "legacy"; + legacyGPT = externalModule "legacy+gpt"; + hybrid = externalModule "hybrid"; + }; + + + testScript = { nodes, ... }: + lib.concatLines (lib.mapAttrsToList (name: node: '' + import os + import subprocess + import tempfile + import shutil + + tmp_disk_image = tempfile.NamedTemporaryFile() + + shutil.copyfile("${node.system.build.diskImage}/nixos.img", tmp_disk_image.name) + + subprocess.run([ + "${node.virtualisation.qemu.package}/bin/qemu-img", + "resize", + "-f", + "raw", + tmp_disk_image.name, + "+32M", + ]) + + # Set NIX_DISK_IMAGE so that the qemu script finds the right disk image. + os.environ['NIX_DISK_IMAGE'] = tmp_disk_image.name + + ${name}.wait_for_unit("growpart.service") + systemd_growpart_logs = ${name}.succeed("journalctl --boot --unit growpart.service") + assert "CHANGED" in systemd_growpart_logs + ${name}.succeed("systemctl restart growpart.service") + systemd_growpart_logs = ${name}.succeed("journalctl --boot --unit growpart.service") + assert "NOCHANGE" in systemd_growpart_logs + + '') nodes); +}