lol

nixos/snapper: support more options

+127 -39
+20
nixos/doc/manual/release-notes/rl-2305.section.md
··· 136 136 137 137 - `services.sourcehut.dispatch` and the corresponding package (`sourcehut.dispatchsrht`) have been removed due to [upstream deprecation](https://sourcehut.org/blog/2022-08-01-dispatch-deprecation-plans/). 138 138 139 + - The attributes used by `services.snapper.configs.<name>` have changed. Migrate from this: 140 + 141 + ```nix 142 + services.snapper.configs.example = { 143 + subvolume = "/example"; 144 + extraConfig = '' 145 + ALLOW_USERS="alice" 146 + ''; 147 + }; 148 + ``` 149 + 150 + to this: 151 + 152 + ```nix 153 + services.snapper.configs.example = { 154 + SUBVOLUME = "/example"; 155 + ALLOW_USERS = [ "alice" ]; 156 + }; 157 + ``` 158 + 139 159 - The [services.snapserver.openFirewall](#opt-services.snapserver.openFirewall) module option default value has been changed from `true` to `false`. You will need to explicitly set this option to `true`, or configure your firewall. 140 160 141 161 - The [services.tmate-ssh-server.openFirewall](#opt-services.tmate-ssh-server.openFirewall) module option default value has been changed from `true` to `false`. You will need to explicitly set this option to `true`, or configure your firewall.
+107 -39
nixos/modules/services/misc/snapper.nix
··· 4 4 5 5 let 6 6 cfg = config.services.snapper; 7 + 8 + mkValue = v: 9 + if isList v then "\"${concatMapStringsSep " " (escape [ "\\" " " ]) v}\"" 10 + else if v == true then "yes" 11 + else if v == false then "no" 12 + else if isString v then "\"${v}\"" 13 + else builtins.toJSON v; 14 + 15 + mkKeyValue = k: v: "${k}=${mkValue v}"; 16 + 17 + # "it's recommended to always specify the filesystem type" -- man snapper-configs 18 + defaultOf = k: if k == "FSTYPE" then null else configOptions.${k}.default or null; 19 + 20 + safeStr = types.strMatching "[^\n\"]*" // { 21 + description = "string without line breaks or quotes"; 22 + descriptionClass = "conjunction"; 23 + }; 24 + 25 + configOptions = { 26 + SUBVOLUME = mkOption { 27 + type = types.path; 28 + description = lib.mdDoc '' 29 + Path of the subvolume or mount point. 30 + This path is a subvolume and has to contain a subvolume named 31 + .snapshots. 32 + See also man:snapper(8) section PERMISSIONS. 33 + ''; 34 + }; 35 + 36 + FSTYPE = mkOption { 37 + type = types.enum [ "btrfs" ]; 38 + default = "btrfs"; 39 + description = lib.mdDoc '' 40 + Filesystem type. Only btrfs is stable and tested. 41 + ''; 42 + }; 43 + 44 + ALLOW_GROUPS = mkOption { 45 + type = types.listOf safeStr; 46 + default = []; 47 + description = lib.mdDoc '' 48 + List of groups allowed to operate with the config. 49 + 50 + Also see the PERMISSIONS section in man:snapper(8). 51 + ''; 52 + }; 53 + 54 + ALLOW_USERS = mkOption { 55 + type = types.listOf safeStr; 56 + default = []; 57 + example = [ "alice" ]; 58 + description = lib.mdDoc '' 59 + List of users allowed to operate with the config. "root" is always 60 + implicitly included. 61 + 62 + Also see the PERMISSIONS section in man:snapper(8). 63 + ''; 64 + }; 65 + 66 + TIMELINE_CLEANUP = mkOption { 67 + type = types.bool; 68 + default = false; 69 + description = lib.mdDoc '' 70 + Defines whether the timeline cleanup algorithm should be run for the config. 71 + ''; 72 + }; 73 + 74 + TIMELINE_CREATE = mkOption { 75 + type = types.bool; 76 + default = false; 77 + description = lib.mdDoc '' 78 + Defines whether hourly snapshots should be created. 79 + ''; 80 + }; 81 + }; 7 82 in 8 83 9 84 { ··· 52 127 example = literalExpression '' 53 128 { 54 129 home = { 55 - subvolume = "/home"; 56 - extraConfig = ''' 57 - ALLOW_USERS="alice" 58 - TIMELINE_CREATE=yes 59 - TIMELINE_CLEANUP=yes 60 - '''; 130 + SUBVOLUME = "/home"; 131 + ALLOW_USERS = [ "alice" ]; 132 + TIMELINE_CREATE = true; 133 + TIMELINE_CLEANUP = true; 61 134 }; 62 135 } 63 136 ''; 64 137 65 138 description = lib.mdDoc '' 66 - Subvolume configuration 139 + Subvolume configuration. Any option mentioned in man:snapper-configs(5) 140 + is valid here, even if NixOS doesn't document it. 67 141 ''; 68 142 69 143 type = types.attrsOf (types.submodule { 70 - options = { 71 - subvolume = mkOption { 72 - type = types.path; 73 - description = lib.mdDoc '' 74 - Path of the subvolume or mount point. 75 - This path is a subvolume and has to contain a subvolume named 76 - .snapshots. 77 - See also man:snapper(8) section PERMISSIONS. 78 - ''; 79 - }; 144 + freeformType = types.attrsOf (types.oneOf [ (types.listOf safeStr) types.bool safeStr types.number ]); 80 145 81 - fstype = mkOption { 82 - type = types.enum [ "btrfs" ]; 83 - default = "btrfs"; 84 - description = lib.mdDoc '' 85 - Filesystem type. Only btrfs is stable and tested. 86 - ''; 87 - }; 88 - 89 - extraConfig = mkOption { 90 - type = types.lines; 91 - default = ""; 92 - description = lib.mdDoc '' 93 - Additional configuration next to SUBVOLUME and FSTYPE. 94 - See man:snapper-configs(5). 95 - ''; 96 - }; 97 - }; 146 + options = configOptions; 98 147 }); 99 148 }; 100 149 }; ··· 117 166 118 167 } 119 168 // (mapAttrs' (name: subvolume: nameValuePair "snapper/configs/${name}" ({ 120 - text = '' 121 - ${subvolume.extraConfig} 122 - FSTYPE="${subvolume.fstype}" 123 - SUBVOLUME="${subvolume.subvolume}" 124 - ''; 169 + text = lib.generators.toKeyValue { inherit mkKeyValue; } (filterAttrs (k: v: v != defaultOf k) subvolume); 125 170 })) cfg.configs) 126 171 // (lib.optionalAttrs (cfg.filters != null) { 127 172 "snapper/filters/default.txt".text = cfg.filters; ··· 181 226 unitConfig.ConditionPathExists = "/etc/snapper/configs/root"; 182 227 }; 183 228 229 + assertions = 230 + concatMap 231 + (name: 232 + let 233 + sub = cfg.configs.${name}; 234 + in 235 + [ { assertion = !(sub ? extraConfig); 236 + message = '' 237 + The option definition `services.snapper.configs.${name}.extraConfig' no longer has any effect; please remove it. 238 + The contents of this option should be migrated to attributes on `services.snapper.configs.${name}'. 239 + ''; 240 + } 241 + ] ++ 242 + map 243 + (attr: { 244 + assertion = !(hasAttr attr sub); 245 + message = '' 246 + The option definition `services.snapper.configs.${name}.${attr}' has been renamed to `services.snapper.configs.${name}.${toUpper attr}'. 247 + ''; 248 + }) 249 + [ "fstype" "subvolume" ] 250 + ) 251 + (attrNames cfg.configs); 184 252 }); 185 253 }