summary refs log tree commit diff
path: root/nixos/modules/services/backup
diff options
context:
space:
mode:
authoroxalica <oxalicc@pm.me>2022-10-18 23:50:44 +0800
committeroxalica <oxalicc@pm.me>2022-10-24 08:52:25 +0800
commit50eb816d2994682b3269205b5cf7a4bdeb4a482c (patch)
treec96b010790ac0d5d7aa4cb5a40b7676d6989ad55 /nixos/modules/services/backup
parent1e684b371cf05300bc2b432f958f285855bac8fb (diff)
nixos/btrbk: fix ordering of subsections and refactor
Diffstat (limited to 'nixos/modules/services/backup')
-rw-r--r--nixos/modules/services/backup/btrbk.nix102
1 files changed, 52 insertions, 50 deletions
diff --git a/nixos/modules/services/backup/btrbk.nix b/nixos/modules/services/backup/btrbk.nix
index f1d58f597c255..b6eb68cc43f12 100644
--- a/nixos/modules/services/backup/btrbk.nix
+++ b/nixos/modules/services/backup/btrbk.nix
@@ -1,72 +1,74 @@
 { config, pkgs, lib, ... }:
 let
   inherit (lib)
+    concatLists
+    concatMap
     concatMapStringsSep
     concatStringsSep
     filterAttrs
-    flatten
     isAttrs
-    isString
     literalExpression
     mapAttrs'
     mapAttrsToList
     mkIf
     mkOption
     optionalString
-    partition
-    typeOf
+    sort
     types
     ;
 
-  cfg = config.services.btrbk;
-  sshEnabled = cfg.sshAccess != [ ];
-  serviceEnabled = cfg.instances != { };
-  attr2Lines = attr:
+  # The priority of an option or section.
+  # The configurations format are order-sensitive. Pairs are added as children of
+  # the last sections if possible, otherwise, they start a new section.
+  # We sort them in topological order:
+  # 1. Leaf pairs.
+  # 2. Sections that may contain (1).
+  # 3. Sections that may contain (1) or (2).
+  # 4. Etc.
+  prioOf = { name, value }:
+    if !isAttrs value then 0 # Leaf options.
+    else {
+      target = 1; # Contains: options.
+      subvolume = 2; # Contains: options, target.
+      volume = 3; # Contains: options, target, subvolume.
+    }.${name} or (throw "Unknow section '${name}'");
+
+  genConfig' = set: concatStringsSep "\n" (genConfig set);
+  genConfig = set:
     let
-      pairs = mapAttrsToList (name: value: { inherit name value; }) attr;
-      isSubsection = value:
-        if isAttrs value then true
-        else if isString value then false
-        else throw "invalid type in btrbk config ${typeOf value}";
-      sortedPairs = partition (x: isSubsection x.value) pairs;
+      pairs = mapAttrsToList (name: value: { inherit name value; }) set;
+      sortedPairs = sort (a: b: prioOf a < prioOf b) pairs;
     in
-    flatten (
-      # non subsections go first
-      (
-        map (pair: [ "${pair.name} ${pair.value}" ]) sortedPairs.wrong
-      )
-      ++ # subsections go last
-      (
-        map
-          (
-            pair:
-            mapAttrsToList
-              (
-                childname: value:
-                  [ "${pair.name} ${childname}" ] ++ (map (x: " " + x) (attr2Lines value))
-              )
-              pair.value
-          )
-          sortedPairs.right
-      )
-    )
-  ;
+      concatMap genPair sortedPairs;
+  genSection = sec: secName: value:
+    [ "${sec} ${secName}" ] ++ map (x: " " + x) (genConfig value);
+  genPair = { name, value }:
+    if !isAttrs value
+    then [ "${name} ${value}" ]
+    else concatLists (mapAttrsToList (genSection name) value);
+
   addDefaults = settings: { backend = "btrfs-progs-sudo"; } // settings;
-  mkConfigFile = settings: concatStringsSep "\n" (attr2Lines (addDefaults settings));
-  mkTestedConfigFile = name: settings:
-    let
-      configFile = pkgs.writeText "btrbk-${name}.conf" (mkConfigFile settings);
-    in
-    pkgs.runCommand "btrbk-${name}-tested.conf" { } ''
-      mkdir foo
-      cp ${configFile} $out
-      if (set +o pipefail; ${pkgs.btrbk}/bin/btrbk -c $out ls foo 2>&1 | grep $out);
-      then
-      echo btrbk configuration is invalid
-      cat $out
-      exit 1
-      fi;
+
+  mkConfigFile = name: settings: pkgs.writeTextFile {
+    name = "btrbk-${name}.conf";
+    text = genConfig' (addDefaults settings);
+    checkPhase = ''
+      set +e
+      ${pkgs.btrbk}/bin/btrbk -c $out dryrun
+      # According to btrbk(1), exit status 2 means parse error
+      # for CLI options or the config file.
+      if [[ $? == 2 ]]; then
+        echo "Btrbk configuration is invalid:"
+        cat $out
+        exit 1
+      fi
+      set -e
     '';
+  };
+
+  cfg = config.services.btrbk;
+  sshEnabled = cfg.sshAccess != [ ];
+  serviceEnabled = cfg.instances != { };
 in
 {
   meta.maintainers = with lib.maintainers; [ oxalica ];
@@ -196,7 +198,7 @@ in
       (
         name: instance: {
           name = "btrbk/${name}.conf";
-          value.source = mkTestedConfigFile name instance.settings;
+          value.source = mkConfigFile name instance.settings;
         }
       )
       cfg.instances;