nixos: refactor tarsnap backup service module

Major changes
- Port to systemd timers: for each archive configuration is created a
  tarsnap@archive-name.timer which triggers the instanced service unit
- Rename the `config` option to `archives`

Minor/superficial improvements
- Restrict tarsnap service capabilities
- Use dirOf builtin
- Set executable bit for owner of tarsnap cache directory
- Set IOSchedulingClass to idle
- Humanize numbers when printing stats
- Rewrite most option descriptions
- Simplify assertion
This commit is contained in:
Joachim Fasting 2015-03-10 08:40:08 +01:00
parent a869c8351c
commit 1bdd12ed69
2 changed files with 88 additions and 89 deletions

View File

@ -112,6 +112,9 @@ in zipModules ([]
# VirtualBox # VirtualBox
++ obsolete [ "services" "virtualbox" "enable" ] [ "services" "virtualboxGuest" "enable" ] ++ obsolete [ "services" "virtualbox" "enable" ] [ "services" "virtualboxGuest" "enable" ]
# Tarsnap
++ obsolete [ "services" "tarsnap" "config" ] [ "services" "tarsnap" "archives" ]
# proxy # proxy
++ obsolete [ "nix" "proxy" ] [ "networking" "proxy" "default" ] ++ obsolete [ "nix" "proxy" ] [ "networking" "proxy" "default" ]

View File

@ -12,6 +12,7 @@ let
keyfile ${config.services.tarsnap.keyfile} keyfile ${config.services.tarsnap.keyfile}
${optionalString cfg.nodump "nodump"} ${optionalString cfg.nodump "nodump"}
${optionalString cfg.printStats "print-stats"} ${optionalString cfg.printStats "print-stats"}
${optionalString cfg.printStats "humanize-numbers"}
${optionalNullStr cfg.checkpointBytes "checkpoint-bytes "+cfg.checkpointBytes} ${optionalNullStr cfg.checkpointBytes "checkpoint-bytes "+cfg.checkpointBytes}
${optionalString cfg.aggressiveNetworking "aggressive-networking"} ${optionalString cfg.aggressiveNetworking "aggressive-networking"}
${concatStringsSep "\n" (map (v: "exclude "+v) cfg.excludes)} ${concatStringsSep "\n" (map (v: "exclude "+v) cfg.excludes)}
@ -27,46 +28,39 @@ in
type = types.bool; type = types.bool;
default = false; default = false;
description = '' description = ''
If enabled, NixOS will periodically create backups of the Enable periodic tarsnap backups.
specified directories using the <literal>tarsnap</literal>
backup service. This installs a <literal>systemd</literal>
service called <literal>tarsnap-backup</literal> which is
periodically run by cron, or you may run it on-demand.
See the Tarsnap <link
xlink:href='http://www.tarsnap.com/gettingstarted.html'>Getting
Started</link> page.
''; '';
}; };
keyfile = mkOption { keyfile = mkOption {
type = types.path; type = types.str;
default = "/root/tarsnap.key"; default = "/root/tarsnap.key";
description = '' description = ''
Path to the keyfile which identifies the machine The keyfile which associates this machine with your tarsnap
associated with your Tarsnap account. This file can account.
be created using the Create the keyfile with <command>tarsnap-keygen</command>.
<literal>tarsnap-keygen</literal> utility, and
providing your Tarsnap login credentials. The keyfile name should be given as a string and not a path, to
avoid the key being copied into the Nix store.
''; '';
}; };
cachedir = mkOption { cachedir = mkOption {
type = types.path; type = types.nullOr types.path;
default = "/var/cache/tarsnap"; default = "/var/cache/tarsnap";
description = '' description = ''
Tarsnap operations use a "cache directory" which The cache allows tarsnap to identify previously stored data
allows Tarsnap to identify which blocks of data have blocks, reducing archival time and bandwidth usage.
been previously stored; this directory is specified
via the <literal>cachedir</literal> option. If the Should the cache become desynchronized or corrupted, tarsnap
cache directory is lost or out of date, tarsnap will refuse to run until you manually rebuild the cache with
creation/deletion operations will exit with an error <command>tarsnap --fsck</command>.
message instructing you to run <literal>tarsnap
--fsck</literal> to regenerate the cache directory. Set to <literal>null</literal> to disable caching.
''; '';
}; };
config = mkOption { archives = mkOption {
type = types.attrsOf (types.submodule ( type = types.attrsOf (types.submodule (
{ {
options = { options = {
@ -74,41 +68,44 @@ in
type = types.bool; type = types.bool;
default = true; default = true;
description = '' description = ''
If set to <literal>true</literal>, then don't Exclude files with the <literal>nodump</literal> flag.
archive files which have the
<literal>nodump</literal> flag set.
''; '';
}; };
printStats = mkOption { printStats = mkOption {
type = types.bool; type = types.bool;
default = true; default = true;
description = "Print statistics when creating archives."; description = ''
Print global archive statistics upon completion.
The output is available via
<command>systemctl status tarsnap@archive-name</command>.
'';
}; };
checkpointBytes = mkOption { checkpointBytes = mkOption {
type = types.nullOr types.str; type = types.nullOr types.str;
default = "1G"; default = "1GB";
description = '' description = ''
Create a checkpoint per a particular amount of Create a checkpoint every <literal>checkpointBytes</literal>
uploaded data. By default, Tarsnap will create of uploaded data (optionally specified using an SI prefix).
checkpoints once per GB of data uploaded. At
minimum, <literal>checkpointBytes</literal> must be
1GB.
Can also be set to <literal>null</literal> to 1GB is the minimum value. A higher value is recommended,
disable checkpointing. as checkpointing is expensive.
Set to <literal>null</literal> to disable checkpointing.
''; '';
}; };
period = mkOption { period = mkOption {
type = types.str; type = types.str;
default = "15 01 * * *"; default = "01:15";
example = "hourly";
description = '' description = ''
This option defines (in the format used by cron) Create archive at this interval.
when tarsnap is run for backups. The default is to
backup the specified paths at 01:15 at night every The format is described in
day. <citerefentry><refentrytitle>systemd.time</refentrytitle>
<manvolnum>7</manvolnum></citerefentry>.
''; '';
}; };
@ -116,11 +113,11 @@ in
type = types.bool; type = types.bool;
default = false; default = false;
description = '' description = ''
Aggressive network behaviour: Use multiple TCP Upload data over multiple TCP connections, potentially
connections when writing archives. Use of this increasing tarsnap's bandwidth utilisation at the cost
option is recommended only in cases where TCP of slowing down all other network traffic. Not
congestion control is known to be the limiting recommended unless TCP congestion is the dominant
factor in upload performance. limiting factor.
''; '';
}; };
@ -134,8 +131,7 @@ in
type = types.listOf types.str; type = types.listOf types.str;
default = []; default = [];
description = '' description = ''
Exclude files and directories matching the specified Exclude files and directories matching these patterns.
patterns.
''; '';
}; };
@ -143,12 +139,10 @@ in
type = types.listOf types.str; type = types.listOf types.str;
default = []; default = [];
description = '' description = ''
Include only files and directories matching the Include only files and directories matching these
specified patterns. patterns (the empty list includes everything).
Note that exclusions specified via Exclusions have precedence over inclusions.
<literal>excludes</literal> take precedence over
inclusions.
''; '';
}; };
@ -156,10 +150,10 @@ in
type = types.bool; type = types.bool;
default = false; default = false;
description = '' description = ''
Attempt to reduce tarsnap memory consumption. This Reduce memory consumption by not caching small files.
option will slow down the process of creating Possibly beneficial if the average file size is smaller
archives, but may help on systems where the average than 1 MB and the number of files is lower than the
size of files being backed up is less than 1 MB. total amount of RAM in KB.
''; '';
}; };
@ -167,11 +161,9 @@ in
type = types.bool; type = types.bool;
default = false; default = false;
description = '' description = ''
Try even harder to reduce tarsnap memory Reduce memory consumption by a factor of 2 beyond what
consumption. This can significantly slow down <literal>lowmem</literal> does, at the cost of significantly
tarsnap, but reduces its memory usage by an slowing down the archiving process.
additional factor of 2 beyond what the
<literal>lowmem</literal> option does.
''; '';
}; };
}; };
@ -188,25 +180,22 @@ in
gamedata = gamedata =
{ directories = [ "/var/lib/minecraft "]; { directories = [ "/var/lib/minecraft "];
period = "*/30 * * * *"; period = "*:30";
}; };
} }
''; '';
description = '' description = ''
Configuration of a Tarsnap archive. In the example, your Tarsnap archive configurations. Each attribute names an archive
machine will have two tarsnap archives: to be created at a given time interval, according to the options
<literal>gamedata</literal> (backed up every 30 minutes) and associated with it. When uploading to the tarsnap server,
<literal>nixos</literal> (backed up at 1:15 AM every night by archive names are suffixed by a 1 second resolution timestamp.
default). You can control individual archive backups using
<literal>systemctl</literal>, using the For each member of the set is created a timer which triggers the
<literal>tarsnap@nixos</literal> or instanced <literal>tarsnap@</literal> service unit. You may use
<literal>tarsnap@gamedata</literal> units. For example, <command>systemctl start tarsnap@archive-name</command> to
<literal>systemctl start tarsnap@nixos</literal> will manually trigger creation of <literal>archive-name</literal> at
immediately create a new NixOS archive. By default, archives any time.
are suffixed with the timestamp of when they were started,
down to second resolution. This means you can use GNU
<literal>sort</literal> to sort output easily.
''; '';
}; };
}; };
@ -216,38 +205,45 @@ in
assertions = assertions =
(mapAttrsToList (name: cfg: (mapAttrsToList (name: cfg:
{ assertion = cfg.directories != []; { assertion = cfg.directories != [];
message = "Must specify directories for Tarsnap to back up"; message = "Must specify paths for tarsnap to back up";
}) cfg.config) ++ }) cfg.archives) ++
(mapAttrsToList (name: cfg: (mapAttrsToList (name: cfg:
{ assertion = cfg.lowmem -> !cfg.verylowmem && (cfg.verylowmem -> !cfg.lowmem); { assertion = !(cfg.lowmem && cfg.verylowmem);
message = "You cannot set both lowmem and verylowmem"; message = "You cannot set both lowmem and verylowmem";
}) cfg.config); }) cfg.archives);
systemd.services."tarsnap@" = { systemd.services."tarsnap@" = {
description = "Tarsnap Backup of '%i'"; description = "Tarsnap archive '%i'";
requires = [ "network.target" ]; requires = [ "network.target" ];
path = [ pkgs.tarsnap pkgs.coreutils ]; path = [ pkgs.tarsnap pkgs.coreutils ];
scriptArgs = "%i"; scriptArgs = "%i";
script = '' script = ''
mkdir -p -m 0755 $(dirname ${cfg.cachedir}) mkdir -p -m 0755 ${dirOf cfg.cachedir}
mkdir -p -m 0600 ${cfg.cachedir} mkdir -p -m 0700 ${cfg.cachedir}
DIRS=`cat /etc/tarsnap/$1.dirs` DIRS=`cat /etc/tarsnap/$1.dirs`
exec tarsnap --configfile /etc/tarsnap/$1.conf -c -f $1-$(date +"%Y%m%d%H%M%S") $DIRS exec tarsnap --configfile /etc/tarsnap/$1.conf -c -f $1-$(date +"%Y%m%d%H%M%S") $DIRS
''; '';
serviceConfig = {
IOSchedulingClass = "idle";
NoNewPrivileges = "true";
CapabilityBoundingSet = "CAP_DAC_READ_SEARCH";
};
}; };
services.cron.systemCronJobs = mapAttrsToList (name: cfg: systemd.timers = mapAttrs' (name: cfg: nameValuePair "tarsnap@${name}"
"${cfg.period} root ${config.systemd.package}/bin/systemctl start tarsnap@${name}" { timerConfig.OnCalendar = cfg.period;
) cfg.config; wantedBy = [ "timers.target" ];
}) cfg.archives;
environment.etc = environment.etc =
(mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.conf" (mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.conf"
{ text = configFile cfg; { text = configFile cfg;
}) cfg.config) // }) cfg.archives) //
(mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.dirs" (mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.dirs"
{ text = concatStringsSep " " cfg.directories; { text = concatStringsSep " " cfg.directories;
}) cfg.config); }) cfg.archives);
environment.systemPackages = [ pkgs.tarsnap ]; environment.systemPackages = [ pkgs.tarsnap ];
}; };