From 9d92bd7845a0fcf895a1e7c4ae95c908be673060 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 24 Sep 2015 18:13:14 +0200 Subject: [PATCH] Add filesystem option to automatically grow to the maximum size This is primarily for EC2 and other cloud environments, where the disk may be bigger than the original image. --- nixos/modules/system/boot/stage-1-init.sh | 15 +++++++++- nixos/modules/system/boot/stage-1.nix | 7 ++++- nixos/modules/tasks/filesystems.nix | 16 ++++++++-- nixos/tests/make-test.nix | 2 +- nixos/tests/resize-root.nix | 36 +++++++++++++++++++++++ 5 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 nixos/tests/resize-root.nix diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh index 480bbfa2b07b..516cbb295fa4 100644 --- a/nixos/modules/system/boot/stage-1-init.sh +++ b/nixos/modules/system/boot/stage-1-init.sh @@ -290,10 +290,23 @@ mountFS() { if [ -z "$fsType" ]; then fsType=auto; fi fi - echo "$device /mnt-root$mountPoint $fsType $options" >> /etc/fstab + # Filter out x- options, which busybox doesn't do yet. + local optionsFiltered="$(IFS=,; for i in $options; do if [ "${i:0:2}" != "x-" ]; then echo -n $i,; fi; done)" + + echo "$device /mnt-root$mountPoint $fsType $optionsFiltered" >> /etc/fstab checkFS "$device" "$fsType" + # Optionally resize the filesystem. + case $options in + *x-nixos.autoresize*) + if [ "$fsType" = ext2 -o "$fsType" = ext3 -o "$fsType" = ext4 ]; then + echo "resizing $device..." + resize2fs "$device" + fi + ;; + esac + # Create backing directories for unionfs-fuse. if [ "$fsType" = unionfs-fuse ]; then for i in $(IFS=:; echo ${options##*,dirs=}); do diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix index f782eca3f647..ace2d10ec9c1 100644 --- a/nixos/modules/system/boot/stage-1.nix +++ b/nixos/modules/system/boot/stage-1.nix @@ -70,6 +70,12 @@ let copy_bin_and_libs ${pkgs.kmod}/bin/kmod ln -sf kmod $out/bin/modprobe + # Copy resize2fs if needed. + ${optionalString (any (fs: fs.autoResize) (attrValues config.fileSystems)) '' + # We need mke2fs in the initrd. + copy_bin_and_libs ${pkgs.e2fsprogs}/sbin/resize2fs + ''} + ${config.boot.initrd.extraUtilsCommands} # Copy ld manually since it isn't detected correctly @@ -393,7 +399,6 @@ in } ]; - system.build.bootStage1 = bootStage1; system.build.initialRamdisk = initialRamdisk; system.build.extraUtils = extraUtils; diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix index ab64106f3533..9dd250f140ce 100644 --- a/nixos/modules/tasks/filesystems.nix +++ b/nixos/modules/tasks/filesystems.nix @@ -7,7 +7,7 @@ let fileSystems = attrValues config.fileSystems; - prioOption = prio: optionalString (prio !=null) " pri=${toString prio}"; + prioOption = prio: optionalString (prio != null) " pri=${toString prio}"; fileSystemOpts = { name, config, ... }: { @@ -43,7 +43,7 @@ let options = mkOption { default = "defaults"; example = "data=journal"; - type = types.commas; + type = types.commas; # FIXME: should be a list description = "Options used to mount the file system."; }; @@ -58,6 +58,17 @@ let ''; }; + autoResize = mkOption { + default = false; + type = types.bool; + description = '' + If set, the filesystem is grown to its maximum size before + being mounted. (This is typically the size of the containing + partition.) This is currently only supported for ext2/3/4 + filesystems that are mounted during early boot. + ''; + }; + noCheck = mkOption { default = false; type = types.bool; @@ -69,6 +80,7 @@ let config = { mountPoint = mkDefault name; device = mkIf (config.fsType == "tmpfs") (mkDefault config.fsType); + options = mkIf config.autoResize "x-nixos.autoresize"; }; }; diff --git a/nixos/tests/make-test.nix b/nixos/tests/make-test.nix index 285ca5b71d6e..f3e26aa7e74d 100644 --- a/nixos/tests/make-test.nix +++ b/nixos/tests/make-test.nix @@ -2,4 +2,4 @@ f: { system ? builtins.currentSystem, ... } @ args: with import ../lib/testing.nix { inherit system; }; -makeTest (if builtins.isFunction f then f (args // { inherit pkgs; }) else f) +makeTest (if builtins.isFunction f then f (args // { inherit pkgs; inherit (pkgs) lib; }) else f) diff --git a/nixos/tests/resize-root.nix b/nixos/tests/resize-root.nix new file mode 100644 index 000000000000..c8ccab38ab6f --- /dev/null +++ b/nixos/tests/resize-root.nix @@ -0,0 +1,36 @@ +import ./make-test.nix ({ pkgs, lib, ...} : { + + meta.maintainers = [ lib.maintainers.eelco ]; + + machine = { config, pkgs, ... }: { + virtualisation.diskSize = 512; + fileSystems = lib.mkVMOverride { + "/".autoResize = true; + }; + }; + + testScript = + '' + # Create a VM with a 512 MiB disk. + $machine->start; + $machine->waitForUnit("multi-user.target"); + my $blocks = $machine->succeed("stat -c %b -f /"); + my $bsize = $machine->succeed("stat -c %S -f /"); + my $size = $blocks * $bsize; + die "wrong free space $size" if $size < 480 * 1024 * 1024 || $size > 512 * 1024 * 1024; + $machine->succeed("touch /marker"); + $machine->shutdown; + + # Grow the disk to 1024 MiB. + system("qemu-img resize vm-state-machine/machine.qcow2 1024M") == 0 or die; + + # Start the VM again and check whether the initrd has correctly + # grown the root filesystem. + $machine->start; + $machine->waitForUnit("multi-user.target"); + $machine->succeed("[ -e /marker ]"); + my $blocks = $machine->succeed("stat -c %b -f /"); + my $size = $blocks * $bsize; + die "wrong free space $size" if $size < 980 * 1024 * 1024 || $size > 1024 * 1024 * 1024; + ''; +})