diff options
author | oxalica <oxalicc@pm.me> | 2022-10-18 23:50:44 +0800 |
---|---|---|
committer | oxalica <oxalicc@pm.me> | 2022-10-24 08:52:25 +0800 |
commit | 50eb816d2994682b3269205b5cf7a4bdeb4a482c (patch) | |
tree | c96b010790ac0d5d7aa4cb5a40b7676d6989ad55 /nixos/modules/services/backup | |
parent | 1e684b371cf05300bc2b432f958f285855bac8fb (diff) |
nixos/btrbk: fix ordering of subsections and refactor
Diffstat (limited to 'nixos/modules/services/backup')
-rw-r--r-- | nixos/modules/services/backup/btrbk.nix | 102 |
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; |