tarsnap service: fix multiple simultaneous archives with a single key
This commit is contained in:
parent
6bb292d42b
commit
15567e6d8e
|
@ -147,6 +147,9 @@ with lib;
|
|||
# parsoid
|
||||
(mkRemovedOptionModule [ "services" "parsoid" "interwikis" ] [ "services" "parsoid" "wikis" ])
|
||||
|
||||
# tarsnap
|
||||
(mkRemovedOptionModule [ "services" "tarsnap" "cachedir" ] "Use services.tarsnap.archives.<name>.cachedir")
|
||||
|
||||
# Options that are obsolete and have no replacement.
|
||||
(mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ] "")
|
||||
(mkRemovedOptionModule [ "programs" "bash" "enable" ] "")
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
{ config, lib, pkgs, utils, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.tarsnap;
|
||||
gcfg = config.services.tarsnap;
|
||||
|
||||
configFile = name: cfg: ''
|
||||
cachedir ${config.services.tarsnap.cachedir}/${name}
|
||||
keyfile ${cfg.keyfile}
|
||||
keyfile ${cfg.keyfile}
|
||||
${optionalString (cfg.cachedir != null) "cachedir ${cfg.cachedir}"}
|
||||
${optionalString cfg.nodump "nodump"}
|
||||
${optionalString cfg.printStats "print-stats"}
|
||||
${optionalString cfg.printStats "humanize-numbers"}
|
||||
${optionalString (cfg.checkpointBytes != null) ("checkpoint-bytes "+cfg.checkpointBytes)}
|
||||
${optionalString cfg.aggressiveNetworking "aggressive-networking"}
|
||||
${concatStringsSep "\n" (map (v: "exclude "+v) cfg.excludes)}
|
||||
${concatStringsSep "\n" (map (v: "include "+v) cfg.includes)}
|
||||
${concatStringsSep "\n" (map (v: "exclude ${v}") cfg.excludes)}
|
||||
${concatStringsSep "\n" (map (v: "include ${v}") cfg.includes)}
|
||||
${optionalString cfg.lowmem "lowmem"}
|
||||
${optionalString cfg.verylowmem "verylowmem"}
|
||||
${optionalString (cfg.maxbw != null) ("maxbw "+toString cfg.maxbw)}
|
||||
${optionalString (cfg.maxbwRateUp != null) ("maxbw-rate-up "+toString cfg.maxbwRateUp)}
|
||||
${optionalString (cfg.maxbwRateDown != null) ("maxbw-rate-down "+toString cfg.maxbwRateDown)}
|
||||
${optionalString (cfg.maxbw != null) "maxbw ${toString cfg.maxbw}"}
|
||||
${optionalString (cfg.maxbwRateUp != null) "maxbw-rate-up ${toString cfg.maxbwRateUp}"}
|
||||
${optionalString (cfg.maxbwRateDown != null) "maxbw-rate-down ${toString cfg.maxbwRateDown}"}
|
||||
'';
|
||||
in
|
||||
{
|
||||
|
@ -60,34 +60,13 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
cachedir = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = "/var/cache/tarsnap";
|
||||
description = ''
|
||||
The cache allows tarsnap to identify previously stored data
|
||||
blocks, reducing archival time and bandwidth usage.
|
||||
|
||||
Should the cache become desynchronized or corrupted, tarsnap
|
||||
will refuse to run until you manually rebuild the cache with
|
||||
<command>tarsnap --fsck</command>.
|
||||
|
||||
Note that each individual archive (specified below) has its own cache
|
||||
directory specified under <literal>cachedir</literal>; this is because
|
||||
tarsnap locks the cache during backups, meaning multiple services
|
||||
archives cannot be backed up concurrently or overlap with a shared
|
||||
cache.
|
||||
|
||||
Set to <literal>null</literal> to disable caching.
|
||||
'';
|
||||
};
|
||||
|
||||
archives = mkOption {
|
||||
type = types.attrsOf (types.submodule (
|
||||
type = types.attrsOf (types.submodule ({ config, ... }:
|
||||
{
|
||||
options = {
|
||||
keyfile = mkOption {
|
||||
type = types.str;
|
||||
default = config.services.tarsnap.keyfile;
|
||||
default = gcfg.keyfile;
|
||||
description = ''
|
||||
Set a specific keyfile for this archive. This defaults to
|
||||
<literal>"/root/tarsnap.key"</literal> if left unspecified.
|
||||
|
@ -107,6 +86,21 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
cachedir = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = "/var/cache/tarsnap/${utils.escapeSystemdPath config.keyfile}";
|
||||
description = ''
|
||||
The cache allows tarsnap to identify previously stored data
|
||||
blocks, reducing archival time and bandwidth usage.
|
||||
|
||||
Should the cache become desynchronized or corrupted, tarsnap
|
||||
will refuse to run until you manually rebuild the cache with
|
||||
<command>tarsnap --fsck</command>.
|
||||
|
||||
Set to <literal>null</literal> to disable caching.
|
||||
'';
|
||||
};
|
||||
|
||||
nodump = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
|
@ -249,7 +243,7 @@ in
|
|||
};
|
||||
|
||||
gamedata =
|
||||
{ directories = [ "/var/lib/minecraft "];
|
||||
{ directories = [ "/var/lib/minecraft" ];
|
||||
period = "*:30";
|
||||
};
|
||||
}
|
||||
|
@ -262,8 +256,8 @@ in
|
|||
archive names are suffixed by a 1 second resolution timestamp.
|
||||
|
||||
For each member of the set is created a timer which triggers the
|
||||
instanced <literal>tarsnap@</literal> service unit. You may use
|
||||
<command>systemctl start tarsnap@archive-name</command> to
|
||||
instanced <literal>tarsnap-archive-name</literal> service unit. You may use
|
||||
<command>systemctl start tarsnap-archive-name</command> to
|
||||
manually trigger creation of <literal>archive-name</literal> at
|
||||
any time.
|
||||
'';
|
||||
|
@ -271,63 +265,73 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
config = mkIf gcfg.enable {
|
||||
assertions =
|
||||
(mapAttrsToList (name: cfg:
|
||||
{ assertion = cfg.directories != [];
|
||||
message = "Must specify paths for tarsnap to back up";
|
||||
}) cfg.archives) ++
|
||||
}) gcfg.archives) ++
|
||||
(mapAttrsToList (name: cfg:
|
||||
{ assertion = !(cfg.lowmem && cfg.verylowmem);
|
||||
message = "You cannot set both lowmem and verylowmem";
|
||||
}) cfg.archives);
|
||||
}) gcfg.archives);
|
||||
|
||||
systemd.services."tarsnap@" = {
|
||||
description = "Tarsnap archive '%i'";
|
||||
requires = [ "network-online.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
systemd.services =
|
||||
mapAttrs' (name: cfg: nameValuePair "tarsnap-${name}" {
|
||||
description = "Tarsnap archive '${name}'";
|
||||
requires = [ "network-online.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
|
||||
path = [ pkgs.iputils pkgs.tarsnap pkgs.coreutils ];
|
||||
path = [ pkgs.iputils pkgs.tarsnap pkgs.utillinux ];
|
||||
|
||||
# In order for the persistent tarsnap timer to work reliably, we have to
|
||||
# make sure that the tarsnap server is reachable after systemd starts up
|
||||
# the service - therefore we sleep in a loop until we can ping the
|
||||
# endpoint.
|
||||
preStart = "while ! ping -q -c 1 v1-0-0-server.tarsnap.com &> /dev/null; do sleep 3; done";
|
||||
scriptArgs = "%i";
|
||||
script = ''
|
||||
mkdir -p -m 0755 ${dirOf cfg.cachedir}
|
||||
mkdir -p -m 0700 ${cfg.cachedir}
|
||||
chown root:root ${cfg.cachedir}
|
||||
chmod 0700 ${cfg.cachedir}
|
||||
mkdir -p -m 0700 ${cfg.cachedir}/$1
|
||||
DIRS=`cat /etc/tarsnap/$1.dirs`
|
||||
exec tarsnap --configfile /etc/tarsnap/$1.conf -c -f $1-$(date +"%Y%m%d%H%M%S") $DIRS
|
||||
'';
|
||||
# In order for the persistent tarsnap timer to work reliably, we have to
|
||||
# make sure that the tarsnap server is reachable after systemd starts up
|
||||
# the service - therefore we sleep in a loop until we can ping the
|
||||
# endpoint.
|
||||
preStart = ''
|
||||
while ! ping -q -c 1 v1-0-0-server.tarsnap.com &> /dev/null; do sleep 3; done
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
IOSchedulingClass = "idle";
|
||||
NoNewPrivileges = "true";
|
||||
CapabilityBoundingSet = "CAP_DAC_READ_SEARCH";
|
||||
PermissionsStartOnly = "true";
|
||||
};
|
||||
};
|
||||
script =
|
||||
let run = ''tarsnap --configfile "/etc/tarsnap/${name}.conf" -c -f "${name}-$(date +"%Y%m%d%H%M%S")" ${concatStringsSep " " cfg.directories}'';
|
||||
in if (cfg.cachedir != null) then ''
|
||||
mkdir -p ${cfg.cachedir}
|
||||
chmod 0700 ${cfg.cachedir}
|
||||
|
||||
( flock 9
|
||||
if [ ! -e ${cfg.cachedir}/firstrun ]; then
|
||||
( flock 10
|
||||
flock -u 9
|
||||
tarsnap --configfile "/etc/tarsnap/${name}.conf" --fsck
|
||||
flock 9
|
||||
) 10>${cfg.cachedir}/firstrun
|
||||
fi
|
||||
) 9>${cfg.cachedir}/lockf
|
||||
|
||||
exec flock ${cfg.cachedir}/firstrun ${run}
|
||||
'' else "exec ${run}";
|
||||
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
IOSchedulingClass = "idle";
|
||||
NoNewPrivileges = "true";
|
||||
CapabilityBoundingSet = [ "CAP_DAC_READ_SEARCH" ];
|
||||
PermissionsStartOnly = "true";
|
||||
};
|
||||
}) gcfg.archives;
|
||||
|
||||
# Note: the timer must be Persistent=true, so that systemd will start it even
|
||||
# if e.g. your laptop was asleep while the latest interval occurred.
|
||||
systemd.timers = mapAttrs' (name: cfg: nameValuePair "tarsnap@${name}"
|
||||
systemd.timers = mapAttrs' (name: cfg: nameValuePair "tarsnap-${name}"
|
||||
{ timerConfig.OnCalendar = cfg.period;
|
||||
timerConfig.Persistent = "true";
|
||||
wantedBy = [ "timers.target" ];
|
||||
}) cfg.archives;
|
||||
}) gcfg.archives;
|
||||
|
||||
environment.etc =
|
||||
(mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.conf"
|
||||
mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.conf"
|
||||
{ text = configFile name cfg;
|
||||
}) cfg.archives) //
|
||||
(mapAttrs' (name: cfg: nameValuePair "tarsnap/${name}.dirs"
|
||||
{ text = concatStringsSep " " cfg.directories;
|
||||
}) cfg.archives);
|
||||
}) gcfg.archives;
|
||||
|
||||
environment.systemPackages = [ pkgs.tarsnap ];
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue