diff options
author | Nick Cao <nickcao@nichi.co> | 2023-01-22 16:23:04 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-22 16:23:04 +0800 |
commit | 99f9998309826651c9e5d6815c8c7239d8438f94 (patch) | |
tree | 9487ddf2fc5ca3f332d5ec6c16dc323797dcf561 /nixos | |
parent | a0ea430baddf72b7933678c41fe1f76f51b94e5b (diff) | |
parent | 9dbdb059245ccdb8e7e8161c6a37301a5397ef6d (diff) |
Merge pull request #204386 from robryk/resticassert
nixos/restic: small enhancements
Diffstat (limited to 'nixos')
-rw-r--r-- | nixos/modules/services/backup/restic.nix | 23 | ||||
-rw-r--r-- | nixos/tests/restic.nix | 45 |
2 files changed, 52 insertions, 16 deletions
diff --git a/nixos/modules/services/backup/restic.nix b/nixos/modules/services/backup/restic.nix index 0d21b1e8d66a8..bc24e13aa050e 100644 --- a/nixos/modules/services/backup/restic.nix +++ b/nixos/modules/services/backup/restic.nix @@ -126,6 +126,21 @@ in ]; }; + exclude = mkOption { + type = types.listOf types.str; + default = [ ]; + description = lib.mdDoc '' + Patterns to exclude when backing up. See + https://restic.readthedocs.io/en/latest/040_backup.html#excluding-files for + details on syntax. + ''; + example = [ + "/var/cache" + "/home/*/.cache" + ".git" + ]; + }; + timerConfig = mkOption { type = types.attrsOf unitOption; default = { @@ -249,6 +264,7 @@ in example = { localbackup = { paths = [ "/home" ]; + exclude = [ "/home/*/.cache" ]; repository = "/mnt/backup-hdd"; passwordFile = "/etc/nixos/secrets/restic-password"; initialize = true; @@ -270,12 +286,17 @@ in config = { warnings = mapAttrsToList (n: v: "services.restic.backups.${n}.s3CredentialsFile is deprecated, please use services.restic.backups.${n}.environmentFile instead.") (filterAttrs (n: v: v.s3CredentialsFile != null) config.services.restic.backups); + assertions = mapAttrsToList (n: v: { + assertion = (v.repository == null) != (v.repositoryFile == null); + message = "services.restic.backups.${n}: exactly one of repository or repositoryFile should be set"; + }) config.services.restic.backups; systemd.services = mapAttrs' (name: backup: let extraOptions = concatMapStrings (arg: " -o ${arg}") backup.extraOptions; resticCmd = "${backup.package}/bin/restic${extraOptions}"; + excludeFlags = if (backup.exclude != []) then ["--exclude-file=${pkgs.writeText "exclude-patterns" (concatStringsSep "\n" backup.exclude)}"] else []; filesFromTmpFile = "/run/restic-backups-${name}/includes"; backupPaths = if (backup.dynamicFilesFrom == null) @@ -311,7 +332,7 @@ in restartIfChanged = false; serviceConfig = { Type = "oneshot"; - ExecStart = (optionals (backupPaths != "") [ "${resticCmd} backup --cache-dir=%C/restic-backups-${name} ${concatStringsSep " " backup.extraBackupArgs} ${backupPaths}" ]) + ExecStart = (optionals (backupPaths != "") [ "${resticCmd} backup --cache-dir=%C/restic-backups-${name} ${concatStringsSep " " (backup.extraBackupArgs ++ excludeFlags)} ${backupPaths}" ]) ++ pruneCmd; User = backup.user; RuntimeDirectory = "restic-backups-${name}"; diff --git a/nixos/tests/restic.nix b/nixos/tests/restic.nix index 3681c4cf190e3..42af0783863ea 100644 --- a/nixos/tests/restic.nix +++ b/nixos/tests/restic.nix @@ -7,17 +7,27 @@ import ./make-test-python.nix ( rcloneRepository = "rclone:local:/tmp/restic-rclone-backup"; backupPrepareCommand = '' - touch /opt/backupPrepareCommand - test ! -e /opt/backupCleanupCommand + touch /tmp/backupPrepareCommand + test ! -e /tmp/backupCleanupCommand ''; backupCleanupCommand = '' - rm /opt/backupPrepareCommand - touch /opt/backupCleanupCommand + rm /tmp/backupPrepareCommand + touch /tmp/backupCleanupCommand ''; + testDir = pkgs.stdenvNoCC.mkDerivation { + name = "test-files-to-backup"; + unpackPhase = "true"; + installPhase = '' + mkdir $out + touch $out/some_file + ''; + }; + passwordFile = "${pkgs.writeText "password" "correcthorsebatterystaple"}"; paths = [ "/opt" ]; + exclude = [ "/opt/excluded_file_*" ]; pruneOpts = [ "--keep-daily 2" "--keep-weekly 1" @@ -38,17 +48,17 @@ import ./make-test-python.nix ( { services.restic.backups = { remotebackup = { - inherit passwordFile paths pruneOpts backupPrepareCommand backupCleanupCommand; + inherit passwordFile paths exclude pruneOpts backupPrepareCommand backupCleanupCommand; repository = remoteRepository; initialize = true; }; remote-from-file-backup = { - inherit passwordFile paths pruneOpts; + inherit passwordFile paths exclude pruneOpts; initialize = true; repositoryFile = pkgs.writeText "repositoryFile" remoteFromFileRepository; }; rclonebackup = { - inherit passwordFile paths pruneOpts; + inherit passwordFile paths exclude pruneOpts; initialize = true; repository = rcloneRepository; rcloneConfig = { @@ -94,16 +104,21 @@ import ./make-test-python.nix ( ) server.succeed( # set up - "mkdir -p /opt", - "touch /opt/some_file", + "cp -rT ${testDir} /opt", + "touch /opt/excluded_file_1 /opt/excluded_file_2", "mkdir -p /tmp/restic-rclone-backup", # test that remotebackup runs custom commands and produces a snapshot "timedatectl set-time '2016-12-13 13:45'", "systemctl start restic-backups-remotebackup.service", - "rm /opt/backupCleanupCommand", + "rm /tmp/backupCleanupCommand", '${pkgs.restic}/bin/restic -r ${remoteRepository} -p ${passwordFile} snapshots --json | ${pkgs.jq}/bin/jq "length | . == 1"', + # test that restoring that snapshot produces the same directory + "mkdir /tmp/restore-1", + "${pkgs.restic}/bin/restic -r ${remoteRepository} -p ${passwordFile} restore latest -t /tmp/restore-1", + "diff -ru ${testDir} /tmp/restore-1/opt", + # test that remote-from-file-backup produces a snapshot "systemctl start restic-backups-remote-from-file-backup.service", '${pkgs.restic}/bin/restic -r ${remoteFromFileRepository} -p ${passwordFile} snapshots --json | ${pkgs.jq}/bin/jq "length | . == 1"', @@ -120,27 +135,27 @@ import ./make-test-python.nix ( # test that we can create four snapshots in remotebackup and rclonebackup "timedatectl set-time '2017-12-13 13:45'", "systemctl start restic-backups-remotebackup.service", - "rm /opt/backupCleanupCommand", + "rm /tmp/backupCleanupCommand", "systemctl start restic-backups-rclonebackup.service", "timedatectl set-time '2018-12-13 13:45'", "systemctl start restic-backups-remotebackup.service", - "rm /opt/backupCleanupCommand", + "rm /tmp/backupCleanupCommand", "systemctl start restic-backups-rclonebackup.service", "timedatectl set-time '2018-12-14 13:45'", "systemctl start restic-backups-remotebackup.service", - "rm /opt/backupCleanupCommand", + "rm /tmp/backupCleanupCommand", "systemctl start restic-backups-rclonebackup.service", "timedatectl set-time '2018-12-15 13:45'", "systemctl start restic-backups-remotebackup.service", - "rm /opt/backupCleanupCommand", + "rm /tmp/backupCleanupCommand", "systemctl start restic-backups-rclonebackup.service", "timedatectl set-time '2018-12-16 13:45'", "systemctl start restic-backups-remotebackup.service", - "rm /opt/backupCleanupCommand", + "rm /tmp/backupCleanupCommand", "systemctl start restic-backups-rclonebackup.service", '${pkgs.restic}/bin/restic -r ${remoteRepository} -p ${passwordFile} snapshots --json | ${pkgs.jq}/bin/jq "length | . == 4"', |