summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--nixos/modules/services/backup/mysql-backup.nix90
-rw-r--r--nixos/release.nix1
-rw-r--r--nixos/tests/mysql-backup.nix42
-rw-r--r--nixos/tests/testdb.sql1
4 files changed, 114 insertions, 20 deletions
diff --git a/nixos/modules/services/backup/mysql-backup.nix b/nixos/modules/services/backup/mysql-backup.nix
index 28f607861f7..3f533fa457d 100644
--- a/nixos/modules/services/backup/mysql-backup.nix
+++ b/nixos/modules/services/backup/mysql-backup.nix
@@ -6,10 +6,28 @@ let
 
   inherit (pkgs) mysql gzip;
 
-  cfg = config.services.mysqlBackup ;
-  location = cfg.location ;
-  mysqlBackupCron = db : ''
-    ${cfg.period} ${cfg.user} ${mysql}/bin/mysqldump ${if cfg.singleTransaction then "--single-transaction" else ""} ${db} | ${gzip}/bin/gzip -c > ${location}/${db}.gz
+  cfg = config.services.mysqlBackup;
+  defaultUser = "mysqlbackup";
+
+  backupScript = ''
+    set -o pipefail
+    failed=""
+    ${concatMapStringsSep "\n" backupDatabaseScript cfg.databases}
+    if [ -n "$failed" ]; then
+      echo "Backup of database(s) failed:$failed"
+      exit 1
+    fi
+  '';
+  backupDatabaseScript = db: ''
+    dest="${cfg.location}/${db}.gz"
+    if ${mysql}/bin/mysqldump ${if cfg.singleTransaction then "--single-transaction" else ""} ${db} | ${gzip}/bin/gzip -c > $dest.tmp; then
+      mv $dest.tmp $dest
+      echo "Backed up to $dest"
+    else
+      echo "Failed to back up to $dest"
+      rm -f $dest.tmp
+      failed="$failed ${db}"
+    fi
   '';
 
 in
@@ -26,17 +44,16 @@ in
         '';
       };
 
-      period = mkOption {
-        default = "15 01 * * *";
+      calendar = mkOption {
+        type = types.str;
+        default = "01:15:00";
         description = ''
-          This option defines (in the format used by cron) when the
-          databases should be dumped.
-          The default is to update at 01:15 (at night) every day.
+          Configured when to run the backup service systemd unit (DayOfWeek Year-Month-Day Hour:Minute:Second).
         '';
       };
 
       user = mkOption {
-        default = "mysql";
+        default = defaultUser;
         description = ''
           User to be used to perform backup.
         '';
@@ -66,16 +83,49 @@ in
 
   };
 
-  config = mkIf config.services.mysqlBackup.enable {
-
-    services.cron.systemCronJobs = map mysqlBackupCron config.services.mysqlBackup.databases;
-
-    system.activationScripts.mysqlBackup = stringAfter [ "stdio" "users" ]
-      ''
-        mkdir -m 0700 -p ${config.services.mysqlBackup.location}
-        chown ${config.services.mysqlBackup.user} ${config.services.mysqlBackup.location}
-      '';
-
+  config = mkIf cfg.enable {
+    users.extraUsers = optionalAttrs (cfg.user == defaultUser) (singleton
+      { name = defaultUser;
+        isSystemUser = true;
+        createHome = false;
+        home = cfg.location;
+        group = "nogroup";
+      });
+
+    services.mysql.ensureUsers = [{
+      name = cfg.user;
+      ensurePermissions = with lib;
+        let
+          privs = "SELECT, SHOW VIEW, TRIGGER, LOCK TABLES";
+          grant = db: nameValuePair "${db}.*" privs;
+        in
+          listToAttrs (map grant cfg.databases);
+    }];
+
+    systemd = {
+      timers."mysql-backup" = {
+        description = "Mysql backup timer";
+        wantedBy = [ "timers.target" ];
+        timerConfig = {
+          OnCalendar = cfg.calendar;
+          AccuracySec = "5m";
+          Unit = "mysql-backup.service";
+        };
+      };
+      services."mysql-backup" = {
+        description = "Mysql backup service";
+        enable = true;
+        serviceConfig = {
+          User = cfg.user;
+          PermissionsStartOnly = true;
+        };
+        preStart = ''
+          mkdir -m 0700 -p ${cfg.location}
+          chown -R ${cfg.user} ${cfg.location}
+        '';
+        script = backupScript;
+      };
+    };
   };
 
 }
diff --git a/nixos/release.nix b/nixos/release.nix
index ac7755a160f..06f1c73410c 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -283,6 +283,7 @@ in rec {
   tests.mumble = callTest tests/mumble.nix {};
   tests.munin = callTest tests/munin.nix {};
   tests.mysql = callTest tests/mysql.nix {};
+  tests.mysqlBackup = callTest tests/mysql-backup.nix {};
   tests.mysqlReplication = callTest tests/mysql-replication.nix {};
   tests.nat.firewall = callTest tests/nat.nix { withFirewall = true; };
   tests.nat.firewall-conntrack = callTest tests/nat.nix { withFirewall = true; withConntrackHelpers = true; };
diff --git a/nixos/tests/mysql-backup.nix b/nixos/tests/mysql-backup.nix
new file mode 100644
index 00000000000..f5bcc460cba
--- /dev/null
+++ b/nixos/tests/mysql-backup.nix
@@ -0,0 +1,42 @@
+# Test whether mysqlBackup option works
+import ./make-test.nix ({ pkgs, ... } : {
+  name = "mysql-backup";
+  meta = with pkgs.stdenv.lib.maintainers; {
+    maintainers = [ rvl ];
+  };
+
+  nodes = {
+    master = { config, pkgs, ... }: {
+      services.mysql = {
+        enable = true;
+        initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ];
+        package = pkgs.mysql;
+      };
+
+      services.mysqlBackup = {
+        enable = true;
+        databases = [ "doesnotexist" "testdb" ];
+      };
+    };
+  };
+
+  testScript =
+    '' startAll;
+
+       # Need to have mysql started so that it can be populated with data.
+       $master->waitForUnit("mysql.service");
+
+       # Wait for testdb to be populated.
+       $master->sleep(10);
+
+       # Do a backup and wait for it to finish.
+       $master->startJob("mysql-backup.service");
+       $master->waitForJob("mysql-backup.service");
+
+       # Check that data appears in backup
+       $master->succeed("${pkgs.gzip}/bin/zcat /var/backup/mysql/testdb.gz | grep hello");
+
+       # Check that a failed backup is logged
+       $master->succeed("journalctl -u mysql-backup.service | grep 'fail.*doesnotexist' > /dev/null");
+    '';
+})
diff --git a/nixos/tests/testdb.sql b/nixos/tests/testdb.sql
index 4fb28fea3df..3c68c49ae82 100644
--- a/nixos/tests/testdb.sql
+++ b/nixos/tests/testdb.sql
@@ -8,3 +8,4 @@ insert into tests values (1, 'a');
 insert into tests values (2, 'b');
 insert into tests values (3, 'c');
 insert into tests values (4, 'd');
+insert into tests values (5, 'hello');