about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorNick Cao <nickcao@nichi.co>2023-01-22 16:23:04 +0800
committerGitHub <noreply@github.com>2023-01-22 16:23:04 +0800
commit99f9998309826651c9e5d6815c8c7239d8438f94 (patch)
tree9487ddf2fc5ca3f332d5ec6c16dc323797dcf561 /nixos
parenta0ea430baddf72b7933678c41fe1f76f51b94e5b (diff)
parent9dbdb059245ccdb8e7e8161c6a37301a5397ef6d (diff)
Merge pull request #204386 from robryk/resticassert
nixos/restic: small enhancements
Diffstat (limited to 'nixos')
-rw-r--r--nixos/modules/services/backup/restic.nix23
-rw-r--r--nixos/tests/restic.nix45
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"',