summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2015-09-24 18:13:14 +0200
committerEelco Dolstra <eelco.dolstra@logicblox.com>2015-09-24 19:59:44 +0200
commit9d92bd7845a0fcf895a1e7c4ae95c908be673060 (patch)
treea5c0990def36969e3bc19d4aa9fc97f7dadcc846
parentf40c7ed1435d9507868337ae7509fe6d0392498b (diff)
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.
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh15
-rw-r--r--nixos/modules/system/boot/stage-1.nix7
-rw-r--r--nixos/modules/tasks/filesystems.nix16
-rw-r--r--nixos/tests/make-test.nix2
-rw-r--r--nixos/tests/resize-root.nix36
5 files changed, 71 insertions, 5 deletions
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
index 480bbfa2b07b5..516cbb295fa42 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 f782eca3f647e..ace2d10ec9c1e 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 ab64106f35337..9dd250f140ceb 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 285ca5b71d6ee..f3e26aa7e74d2 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 0000000000000..c8ccab38ab6f0
--- /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;
+    '';
+})