From 19d2ef374ccf7704f9809611a58543fd50297e00 Mon Sep 17 00:00:00 2001
From: prescientmoon <git@moonythm.dev>
Date: Tue, 27 Aug 2024 20:52:29 +0200
Subject: [PATCH] New rollback script

---
 hosts/nixos/calypso/filesystems/default.nix   | 49 +++++++++++++++----
 .../optional/services/restic/default.nix      | 42 +++++++++-------
 2 files changed, 65 insertions(+), 26 deletions(-)

diff --git a/hosts/nixos/calypso/filesystems/default.nix b/hosts/nixos/calypso/filesystems/default.nix
index 154d7ec..dd8cac2 100644
--- a/hosts/nixos/calypso/filesystems/default.nix
+++ b/hosts/nixos/calypso/filesystems/default.nix
@@ -1,4 +1,4 @@
-{ lib, pkgs, ... }:
+{ lib, ... }:
 {
   imports = [ (import ./partitions.nix { }) ];
 
@@ -22,18 +22,49 @@
   # }}}
   # {{{ Rollback
   boot.initrd.systemd.services.rollback = {
-    path = [ pkgs.btrfs-progs ];
-    serviceConfig = {
-      Type = "oneshot";
-      RemainAfterExit = true;
-    };
-    unitConfig.DefaultDependencies = "no";
+    description = "Rollback BTRFS root subvolume to a pristine state";
     wantedBy = [ "initrd.target" ];
     after = [ "systemd-cryptsetup@enc.service" ];
     before = [ "sysroot.mount" ];
+    unitConfig.DefaultDependencies = "no";
+    serviceConfig.Type = "oneshot";
     script = ''
-      btrfs subvolume delete /
-      btrfs subvolume create /
+      mkdir -p /mnt
+
+      # We first mount the btrfs root to /mnt
+      # so we can manipulate btrfs subvolumes.
+      mount -o subvol=/ /dev/mapper/enc /mnt
+
+      # While we're tempted to just delete /root and create
+      # a new snapshot from /root-blank, /root is already
+      # populated at this point with a number of subvolumes,
+      # which makes `btrfs subvolume delete` fail.
+      # So, we remove them first.
+      #
+      # /root contains subvolumes:
+      # - /root/var/lib/portables
+      # - /root/var/lib/machines
+      #
+      # I suspect these are related to systemd-nspawn, but
+      # since I don't use it I'm not 100% sure.
+      # Anyhow, deleting these subvolumes hasn't resulted
+      # in any issues so far, except for fairly
+      # benign-looking errors from systemd-tmpfiles.
+      btrfs subvolume list -o /mnt/root |
+        cut -f9 -d' ' |
+        while read subvolume; do
+          echo "deleting /$subvolume subvolume..."
+          btrfs subvolume delete "/mnt/$subvolume"
+        done &&
+        echo "deleting /root subvolume..." &&
+        btrfs subvolume delete /mnt/root
+
+      echo "restoring blank /root subvolume..."
+      btrfs subvolume snapshot /mnt/root-blank /mnt/root
+
+      # Once we're done rolling back to a blank snapshot,
+      # we can unmount /mnt and continue on the boot process.
+      umount /mnt
     '';
   };
   # }}}
diff --git a/hosts/nixos/common/optional/services/restic/default.nix b/hosts/nixos/common/optional/services/restic/default.nix
index 0cefef0..d25d6fb 100644
--- a/hosts/nixos/common/optional/services/restic/default.nix
+++ b/hosts/nixos/common/optional/services/restic/default.nix
@@ -3,24 +3,32 @@ let
   backupUrl = lib.removeSuffix "\n" (builtins.readFile ./url.txt);
 
   # {{{ Backup helper
-  createBackup = { name, paths, exclude, pruneOpts }: {
-    inherit pruneOpts paths;
+  createBackup =
+    {
+      name,
+      paths,
+      exclude,
+      pruneOpts,
+    }:
+    {
+      inherit pruneOpts paths;
 
-    initialize = true;
-    repository = "sftp:${backupUrl}:backups/${name}";
-    passwordFile = config.sops.secrets.backup_password.path;
-    extraOptions = [ "sftp.args='-i ${config.users.users.pilot.home}/.ssh/id_ed25519'" ];
+      initialize = true;
+      repository = "sftp:${backupUrl}:backups/${name}";
+      passwordFile = config.sops.secrets.backup_password.path;
+      extraOptions = [ "sftp.args='-i ${config.users.users.pilot.home}/.ssh/id_ed25519'" ];
 
-    exclude = [
-      # Syncthing / direnv / git stuff
-      ".direnv"
-      ".git"
-      ".stfolder"
-      ".stversions"
-    ] ++ exclude;
-  };
-  # }}}
+      exclude = [
+        # Syncthing / direnv / git / snapper stuff
+        ".direnv"
+        ".git"
+        ".stfolder"
+        ".stversions"
+        ".snapshots"
+      ] ++ exclude;
+    };
 in
+# }}}
 {
   sops.secrets.backup_password.sopsFile = ../../../secrets.yaml;
 
@@ -54,7 +62,8 @@ in
 
       paths = [ "/persist/state" ];
       exclude =
-        let home = "/persist/state/${config.users.users.pilot.home}";
+        let
+          home = "/persist/state/${config.users.users.pilot.home}";
         in
         [
           "${home}/discord" # There's lots of cache stored in here
@@ -64,4 +73,3 @@ in
     # }}}
   };
 }
-