Merge pull request #212087 from robryk/resticpaths

nixos/backups/restic: handle cases when both dynamicFileFrom and paths are set
This commit is contained in:
Peder Bergebakken Sundt 2023-10-26 19:35:22 +02:00 committed by GitHub
commit adcaf3962d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 29 additions and 14 deletions

View File

@ -113,12 +113,15 @@ in
}; };
paths = mkOption { paths = mkOption {
# This is nullable for legacy reasons only. We should consider making it a pure listOf
# after some time has passed since this comment was added.
type = types.nullOr (types.listOf types.str); type = types.nullOr (types.listOf types.str);
default = null; default = [ ];
description = lib.mdDoc '' description = lib.mdDoc ''
Which paths to backup. If null or an empty array, no Which paths to backup, in addition to ones specified via
backup command will be run. This can be used to create a `dynamicFilesFrom`. If null or an empty array and
prune-only job. `dynamicFilesFrom` is also null, no backup command will be run.
This can be used to create a prune-only job.
''; '';
example = [ example = [
"/var/lib/postgresql" "/var/lib/postgresql"
@ -231,7 +234,7 @@ in
description = lib.mdDoc '' description = lib.mdDoc ''
A script that produces a list of files to back up. The A script that produces a list of files to back up. The
results of this command are given to the '--files-from' results of this command are given to the '--files-from'
option. option. The result is merged with paths specified via `paths`.
''; '';
example = "find /home/matt/git -type d -name .git"; example = "find /home/matt/git -type d -name .git";
}; };
@ -310,10 +313,7 @@ in
resticCmd = "${backup.package}/bin/restic${extraOptions}"; resticCmd = "${backup.package}/bin/restic${extraOptions}";
excludeFlags = optional (backup.exclude != []) "--exclude-file=${pkgs.writeText "exclude-patterns" (concatStringsSep "\n" backup.exclude)}"; excludeFlags = optional (backup.exclude != []) "--exclude-file=${pkgs.writeText "exclude-patterns" (concatStringsSep "\n" backup.exclude)}";
filesFromTmpFile = "/run/restic-backups-${name}/includes"; filesFromTmpFile = "/run/restic-backups-${name}/includes";
backupPaths = doBackup = (backup.dynamicFilesFrom != null) || (backup.paths != null && backup.paths != []);
if (backup.dynamicFilesFrom == null)
then optionalString (backup.paths != null) (concatStringsSep " " backup.paths)
else "--files-from ${filesFromTmpFile}";
pruneCmd = optionals (builtins.length backup.pruneOpts > 0) [ pruneCmd = optionals (builtins.length backup.pruneOpts > 0) [
(resticCmd + " forget --prune " + (concatStringsSep " " backup.pruneOpts)) (resticCmd + " forget --prune " + (concatStringsSep " " backup.pruneOpts))
(resticCmd + " check " + (concatStringsSep " " backup.checkOpts)) (resticCmd + " check " + (concatStringsSep " " backup.checkOpts))
@ -348,7 +348,7 @@ in
after = [ "network-online.target" ]; after = [ "network-online.target" ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
ExecStart = (optionals (backupPaths != "") [ "${resticCmd} backup ${concatStringsSep " " (backup.extraBackupArgs ++ excludeFlags)} ${backupPaths}" ]) ExecStart = (optionals doBackup [ "${resticCmd} backup ${concatStringsSep " " (backup.extraBackupArgs ++ excludeFlags)} --files-from=${filesFromTmpFile}" ])
++ pruneCmd; ++ pruneCmd;
User = backup.user; User = backup.user;
RuntimeDirectory = "restic-backups-${name}"; RuntimeDirectory = "restic-backups-${name}";
@ -366,8 +366,11 @@ in
${optionalString (backup.initialize) '' ${optionalString (backup.initialize) ''
${resticCmd} snapshots || ${resticCmd} init ${resticCmd} snapshots || ${resticCmd} init
''} ''}
${optionalString (backup.paths != null && backup.paths != []) ''
cat ${pkgs.writeText "staticPaths" (concatStringsSep "\n" backup.paths)} >> ${filesFromTmpFile}
''}
${optionalString (backup.dynamicFilesFrom != null) '' ${optionalString (backup.dynamicFilesFrom != null) ''
${pkgs.writeScript "dynamicFilesFromScript" backup.dynamicFilesFrom} > ${filesFromTmpFile} ${pkgs.writeScript "dynamicFilesFromScript" backup.dynamicFilesFrom} >> ${filesFromTmpFile}
''} ''}
''; '';
} // optionalAttrs (backup.dynamicFilesFrom != null || backup.backupCleanupCommand != null) { } // optionalAttrs (backup.dynamicFilesFrom != null || backup.backupCleanupCommand != null) {

View File

@ -21,7 +21,10 @@ import ./make-test-python.nix (
unpackPhase = "true"; unpackPhase = "true";
installPhase = '' installPhase = ''
mkdir $out mkdir $out
touch $out/some_file echo some_file > $out/some_file
echo some_other_file > $out/some_other_file
mkdir $out/a_dir
echo a_file > $out/a_dir/a_file
''; '';
}; };
@ -53,9 +56,13 @@ import ./make-test-python.nix (
initialize = true; initialize = true;
}; };
remote-from-file-backup = { remote-from-file-backup = {
inherit passwordFile paths exclude pruneOpts; inherit passwordFile exclude pruneOpts;
initialize = true; initialize = true;
repositoryFile = pkgs.writeText "repositoryFile" remoteFromFileRepository; repositoryFile = pkgs.writeText "repositoryFile" remoteFromFileRepository;
paths = [ "/opt/a_dir" ];
dynamicFilesFrom = ''
find /opt -mindepth 1 -maxdepth 1 ! -name a_dir # all files in /opt except for a_dir
'';
}; };
rclonebackup = { rclonebackup = {
inherit passwordFile paths exclude pruneOpts; inherit passwordFile paths exclude pruneOpts;
@ -123,13 +130,18 @@ import ./make-test-python.nix (
"systemctl start restic-backups-remote-from-file-backup.service", "systemctl start restic-backups-remote-from-file-backup.service",
'restic-remote-from-file-backup snapshots --json | ${pkgs.jq}/bin/jq "length | . == 1"', 'restic-remote-from-file-backup snapshots --json | ${pkgs.jq}/bin/jq "length | . == 1"',
# test that restoring that snapshot produces the same directory
"mkdir /tmp/restore-2",
"${pkgs.restic}/bin/restic -r ${remoteRepository} -p ${passwordFile} restore latest -t /tmp/restore-2",
"diff -ru ${testDir} /tmp/restore-2/opt",
# test that rclonebackup produces a snapshot # test that rclonebackup produces a snapshot
"systemctl start restic-backups-rclonebackup.service", "systemctl start restic-backups-rclonebackup.service",
'restic-rclonebackup snapshots --json | ${pkgs.jq}/bin/jq "length | . == 1"', 'restic-rclonebackup snapshots --json | ${pkgs.jq}/bin/jq "length | . == 1"',
# test that custompackage runs both `restic backup` and `restic check` with reasonable commandlines # test that custompackage runs both `restic backup` and `restic check` with reasonable commandlines
"systemctl start restic-backups-custompackage.service", "systemctl start restic-backups-custompackage.service",
"grep 'backup.* /opt' /root/fake-restic.log", "grep 'backup' /root/fake-restic.log",
"grep 'check.* --some-check-option' /root/fake-restic.log", "grep 'check.* --some-check-option' /root/fake-restic.log",
# test that we can create four snapshots in remotebackup and rclonebackup # test that we can create four snapshots in remotebackup and rclonebackup