at master 6.3 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 cfg = config.services.borgmatic; 9 settingsFormat = pkgs.formats.yaml { }; 10 11 postgresql = config.services.postgresql.package; 12 mysql = config.services.mysql.package; 13 requireSudo = 14 s: 15 s ? postgresql_databases 16 && lib.any (d: d ? username && !(d ? password) && !(d ? pg_dump_command)) s.postgresql_databases; 17 addRequiredBinaries = 18 s: 19 s 20 // (lib.optionalAttrs (s ? postgresql_databases && s.postgresql_databases != [ ]) { 21 postgresql_databases = map ( 22 d: 23 let 24 as_user = if d ? username && !(d ? password) then "${pkgs.sudo}/bin/sudo -u ${d.username} " else ""; 25 in 26 { 27 pg_dump_command = 28 if d.name == "all" && (!(d ? format) || isNull d.format) then 29 "${as_user}${postgresql}/bin/pg_dumpall" 30 else 31 "${as_user}${postgresql}/bin/pg_dump"; 32 pg_restore_command = "${as_user}${postgresql}/bin/pg_restore"; 33 psql_command = "${as_user}${postgresql}/bin/psql"; 34 } 35 // d 36 ) s.postgresql_databases; 37 }) 38 // (lib.optionalAttrs (s ? mariadb_databases && s.mariadb_databases != [ ]) { 39 mariadb_databases = map ( 40 d: 41 { 42 mariadb_dump_command = "${mysql}/bin/mariadb-dump"; 43 mariadb_command = "${mysql}/bin/mariadb"; 44 } 45 // d 46 ) s.mariadb_databases; 47 }) 48 // (lib.optionalAttrs (s ? mysql_databases && s.mysql_databases != [ ]) { 49 mysql_databases = map ( 50 d: 51 { 52 mysql_dump_command = "${mysql}/bin/mysqldump"; 53 mysql_command = "${mysql}/bin/mysql"; 54 } 55 // d 56 ) s.mysql_databases; 57 }); 58 59 repository = 60 with lib.types; 61 submodule { 62 options = { 63 path = lib.mkOption { 64 type = str; 65 description = '' 66 Path to the repository 67 ''; 68 }; 69 label = lib.mkOption { 70 type = str; 71 description = '' 72 Label to the repository 73 ''; 74 }; 75 }; 76 }; 77 cfgType = 78 with lib.types; 79 submodule { 80 freeformType = settingsFormat.type; 81 options = { 82 source_directories = lib.mkOption { 83 type = listOf str; 84 default = [ ]; 85 description = '' 86 List of source directories and files to backup. Globs and tildes are 87 expanded. Do not backslash spaces in path names. 88 ''; 89 example = [ 90 "/home" 91 "/etc" 92 "/var/log/syslog*" 93 "/home/user/path with spaces" 94 ]; 95 }; 96 repositories = lib.mkOption { 97 type = listOf repository; 98 default = [ ]; 99 description = '' 100 A required list of local or remote repositories with paths and 101 optional labels (which can be used with the --repository flag to 102 select a repository). Tildes are expanded. Multiple repositories are 103 backed up to in sequence. Borg placeholders can be used. See the 104 output of "borg help placeholders" for details. See ssh_command for 105 SSH options like identity file or port. If systemd service is used, 106 then add local repository paths in the systemd service file to the 107 ReadWritePaths list. 108 ''; 109 example = [ 110 { 111 path = "ssh://user@backupserver/./sourcehostname.borg"; 112 label = "backupserver"; 113 } 114 { 115 path = "/mnt/backup"; 116 label = "local"; 117 } 118 ]; 119 }; 120 }; 121 }; 122 123 cfgfile = settingsFormat.generate "config.yaml" (addRequiredBinaries cfg.settings); 124 125 anycfgRequiresSudo = 126 requireSudo cfg.settings || lib.any requireSudo (lib.attrValues cfg.configurations); 127in 128{ 129 options.services.borgmatic = { 130 enable = lib.mkEnableOption "borgmatic"; 131 132 settings = lib.mkOption { 133 description = '' 134 See <https://torsion.org/borgmatic/docs/reference/configuration/> 135 ''; 136 default = null; 137 type = lib.types.nullOr cfgType; 138 }; 139 140 configurations = lib.mkOption { 141 description = '' 142 Set of borgmatic configurations, see <https://torsion.org/borgmatic/docs/reference/configuration/> 143 ''; 144 default = { }; 145 type = lib.types.attrsOf cfgType; 146 }; 147 148 enableConfigCheck = lib.mkEnableOption "checking all configurations during build time" // { 149 default = true; 150 }; 151 }; 152 153 config = 154 let 155 configFiles = 156 (lib.optionalAttrs (cfg.settings != null) { 157 "borgmatic/config.yaml".source = cfgfile; 158 }) 159 // lib.mapAttrs' ( 160 name: value: 161 lib.nameValuePair "borgmatic.d/${name}.yaml" { 162 source = settingsFormat.generate "${name}.yaml" (addRequiredBinaries value); 163 } 164 ) cfg.configurations; 165 borgmaticCheck = 166 name: f: 167 pkgs.runCommandCC "${name} validation" { } '' 168 ${pkgs.borgmatic}/bin/borgmatic -c ${f.source} config validate 169 touch $out 170 ''; 171 in 172 lib.mkIf cfg.enable { 173 174 warnings = 175 [ ] 176 ++ 177 lib.optional (cfg.settings != null && cfg.settings ? location) 178 "`services.borgmatic.settings.location` is deprecated, please move your options out of sections to the global scope" 179 ++ 180 lib.optional (lib.catAttrs "location" (lib.attrValues cfg.configurations) != [ ]) 181 "`services.borgmatic.configurations.<name>.location` is deprecated, please move your options out of sections to the global scope"; 182 183 environment.systemPackages = [ pkgs.borgmatic ]; 184 185 environment.etc = configFiles; 186 187 systemd.packages = [ pkgs.borgmatic ]; 188 systemd.services.borgmatic.path = [ pkgs.coreutils ]; 189 systemd.services.borgmatic.serviceConfig = lib.optionalAttrs anycfgRequiresSudo { 190 NoNewPrivileges = false; 191 CapabilityBoundingSet = "CAP_DAC_READ_SEARCH CAP_NET_RAW CAP_SETUID CAP_SETGID"; 192 }; 193 194 # Workaround: https://github.com/NixOS/nixpkgs/issues/81138 195 systemd.timers.borgmatic.wantedBy = [ "timers.target" ]; 196 197 system.checks = lib.mkIf cfg.enableConfigCheck (lib.mapAttrsToList borgmaticCheck configFiles); 198 }; 199}