about summary refs log tree commit diff
path: root/nixos/tests/systemd-boot.nix
diff options
context:
space:
mode:
authorJulien Malka <julien@malka.sh>2024-01-06 00:30:23 +0000
committerJulien Malka <julien@malka.sh>2024-01-07 11:34:09 +0000
commiteb435897a6de9c1924b0fc45147470bb557ae105 (patch)
tree24711ab5a70fead677042c6eeac2e79bda957d1c /nixos/tests/systemd-boot.nix
parent67ebfe5a801ec6b0f2fafac2f3a42939e9804f80 (diff)
nixos/systemd-boot: init boot counting
Diffstat (limited to 'nixos/tests/systemd-boot.nix')
-rw-r--r--nixos/tests/systemd-boot.nix157
1 files changed, 152 insertions, 5 deletions
diff --git a/nixos/tests/systemd-boot.nix b/nixos/tests/systemd-boot.nix
index c0b37a230df0f..6e956ab74fbe0 100644
--- a/nixos/tests/systemd-boot.nix
+++ b/nixos/tests/systemd-boot.nix
@@ -13,9 +13,11 @@ let
     boot.loader.systemd-boot.enable = true;
     boot.loader.efi.canTouchEfiVariables = true;
     environment.systemPackages = [ pkgs.efibootmgr ];
+    # Needed for machine-id to be persisted between reboots
+    environment.etc."machine-id".text = "00000000000000000000000000000000";
   };
 in
-{
+rec {
   basic = makeTest {
     name = "systemd-boot";
     meta.maintainers = with pkgs.lib.maintainers; [ danielfullmer julienmalka ];
@@ -252,15 +254,15 @@ in
     '';
   };
 
-  garbage-collect-entry = makeTest {
-    name = "systemd-boot-garbage-collect-entry";
+  garbage-collect-entry = { withBootCounting ? false, ... }: makeTest {
+    name = "systemd-boot-garbage-collect-entry" + optionalString withBootCounting "-with-boot-counting";
     meta.maintainers = with pkgs.lib.maintainers; [ julienmalka ];
 
     nodes = {
       inherit common;
       machine = { pkgs, nodes, ... }: {
         imports = [ common ];
-
+        boot.loader.systemd-boot.bootCounting.enable = withBootCounting;
         # These are configs for different nodes, but we'll use them here in `machine`
         system.extraDependencies = [
           nodes.common.system.build.toplevel
@@ -275,8 +277,12 @@ in
       ''
         machine.succeed("nix-env -p /nix/var/nix/profiles/system --set ${baseSystem}")
         machine.succeed("nix-env -p /nix/var/nix/profiles/system --delete-generations 1")
+        # At this point generation 1 has already been marked as good so we reintroduce counters artificially
+        ${optionalString withBootCounting ''
+        machine.succeed("mv /boot/loader/entries/nixos-generation-1.conf /boot/loader/entries/nixos-generation-1+3.conf")
+        ''}
         machine.succeed("${baseSystem}/bin/switch-to-configuration boot")
-        machine.fail("test -e /boot/loader/entries/nixos-generation-1.conf")
+        machine.fail("test -e /boot/loader/entries/nixos-generation-1*")
         machine.succeed("test -e /boot/loader/entries/nixos-generation-2.conf")
       '';
   };
@@ -322,4 +328,145 @@ in
         machine.wait_for_unit("multi-user.target")
       '';
     };
+
+  # Check that we are booting the default entry and not the generation with largest version number
+  defaultEntry = { withBootCounting ? false, ... }: makeTest {
+    name = "systemd-boot-default-entry" + optionalString withBootCounting "-with-boot-counting";
+    meta.maintainers = with pkgs.lib.maintainers; [ julienmalka ];
+
+    nodes = {
+      machine = { pkgs, lib, nodes, ... }: {
+        imports = [ common ];
+        system.extraDependencies = [ nodes.other_machine.system.build.toplevel ];
+        boot.loader.systemd-boot.bootCounting.enable = withBootCounting;
+      };
+
+      other_machine = { pkgs, lib, ... }: {
+        imports = [ common ];
+        boot.loader.systemd-boot.bootCounting.enable = withBootCounting;
+        environment.systemPackages = [ pkgs.hello ];
+      };
+    };
+    testScript = { nodes, ... }:
+      let
+        orig = nodes.machine.system.build.toplevel;
+        other = nodes.other_machine.system.build.toplevel;
+      in
+      ''
+        orig = "${orig}"
+        other = "${other}"
+
+        def check_current_system(system_path):
+            machine.succeed(f'test $(readlink -f /run/current-system) = "{system_path}"')
+
+        machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf")
+        check_current_system(orig)
+
+        # Switch to other configuration
+        machine.succeed("nix-env -p /nix/var/nix/profiles/system --set ${other}")
+        machine.succeed(f"{other}/bin/switch-to-configuration boot")
+        # Rollback, default entry is now generation 1
+        machine.succeed("nix-env -p /nix/var/nix/profiles/system --rollback")
+        machine.succeed(f"{orig}/bin/switch-to-configuration boot")
+
+        machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf")
+        ${if withBootCounting
+        then ''machine.succeed("test -e /boot/loader/entries/nixos-generation-2+3.conf")''
+        else ''machine.succeed("test -e /boot/loader/entries/nixos-generation-2.conf")''}
+        machine.shutdown()
+
+        machine.start()
+        machine.wait_for_unit("multi-user.target")
+        # Check that we booted generation 1 (default)
+        # even though generation 2 comes first in alphabetical order
+        check_current_system(orig)
+      '';
+  };
+
+
+  bootCounting =
+    let
+      baseConfig = { pkgs, lib, ... }: {
+        imports = [ common ];
+        boot.loader.systemd-boot.bootCounting.enable = true;
+        boot.loader.systemd-boot.bootCounting.trials = 2;
+      };
+    in
+    makeTest {
+      name = "systemd-boot-counting";
+      meta.maintainers = with pkgs.lib.maintainers; [ julienmalka ];
+
+      nodes = {
+        machine = { pkgs, lib, nodes, ... }: {
+          imports = [ baseConfig ];
+          system.extraDependencies = [ nodes.bad_machine.system.build.toplevel ];
+        };
+
+        bad_machine = { pkgs, lib, ... }: {
+          imports = [ baseConfig ];
+
+          systemd.services."failing" = {
+            script = "exit 1";
+            requiredBy = [ "boot-complete.target" ];
+            before = [ "boot-complete.target" ];
+            serviceConfig.Type = "oneshot";
+          };
+        };
+      };
+      testScript = { nodes, ... }:
+        let
+          orig = nodes.machine.system.build.toplevel;
+          bad = nodes.bad_machine.system.build.toplevel;
+        in
+        ''
+          orig = "${orig}"
+          bad = "${bad}"
+
+          def check_current_system(system_path):
+              machine.succeed(f'test $(readlink -f /run/current-system) = "{system_path}"')
+
+          # Ensure we booted using an entry with counters enabled
+          machine.succeed(
+              "test -e /sys/firmware/efi/efivars/LoaderBootCountPath-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"
+          )
+
+          # systemd-bless-boot should have already removed the "+2" suffix from the boot entry
+          machine.wait_for_unit("systemd-bless-boot.service")
+          machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf")
+          check_current_system(orig)
+
+          # Switch to bad configuration
+          machine.succeed("nix-env -p /nix/var/nix/profiles/system --set ${bad}")
+          machine.succeed(f"{bad}/bin/switch-to-configuration boot")
+
+          # Ensure new bootloader entry has initialized counter
+          machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf")
+          machine.succeed("test -e /boot/loader/entries/nixos-generation-2+2.conf")
+          machine.shutdown()
+
+          machine.start()
+          machine.wait_for_unit("multi-user.target")
+          check_current_system(bad)
+          machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf")
+          machine.succeed("test -e /boot/loader/entries/nixos-generation-2+1-1.conf")
+          machine.shutdown()
+
+          machine.start()
+          machine.wait_for_unit("multi-user.target")
+          check_current_system(bad)
+          machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf")
+          machine.succeed("test -e /boot/loader/entries/nixos-generation-2+0-2.conf")
+          machine.shutdown()
+
+          # Should boot back into original configuration
+          machine.start()
+          check_current_system(orig)
+          machine.wait_for_unit("multi-user.target")
+          machine.succeed("test -e /boot/loader/entries/nixos-generation-1.conf")
+          machine.succeed("test -e /boot/loader/entries/nixos-generation-2+0-2.conf")
+          machine.shutdown()
+        '';
+    };
+  defaultEntryWithBootCounting = defaultEntry { withBootCounting = true; };
+  garbageCollectEntryWithBootCounting = garbage-collect-entry { withBootCounting = true; };
 }