Skip to content

Commit a2b0de2

Browse files
committed
rustic: simplify module, drop sources injection
- pass settings through verbatim (drop genProfileConfig, label, sources) - wrapper: use `-P` profile + shared cache dir - drop prometheus passwordFile (use env file) - remove setuid wrappers block - move sources into backup.snapshots in host configs
1 parent bca43d4 commit a2b0de2

2 files changed

Lines changed: 43 additions & 84 deletions

File tree

.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@ indent_size = 2
77
indent_size = 4
88

99
[*.json]
10+
indent_size = 2
11+
12+
[*.yml|*.yaml]
1013
indent_size = 2

nix/modules/rustic.nix

Lines changed: 40 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
/*
2+
TODO:
3+
4+
- handle backup overlap
5+
*/
6+
17
{ ... }: {
28
flake.customModules.rustic =
39
{
@@ -14,48 +20,29 @@
1420
filterAttrs
1521
mapAttrs'
1622
nameValuePair
23+
mapAttrsToList
24+
optionalString
1725
types
1826
;
1927

2028
format = pkgs.formats.toml { };
2129

2230
cfg = config.services.rustic;
2331

24-
# Merge profile options into a TOML attrset.
25-
# `snapshot` key from settings → injected into `backup.snapshots` (array of tables).
26-
# `label` and `sources` come from profile options, not from settings.
27-
genProfileConfig =
28-
name: p:
29-
let
30-
label = if p.label == null then name else p.label;
31-
baseCfg = builtins.removeAttrs p.settings [ "snapshot" ];
32-
snapEntry = {
33-
inherit label;
34-
sources = p.sources;
35-
}
36-
// (builtins.removeAttrs (p.settings.snapshot or { }) [
37-
"label"
38-
"sources"
39-
]);
40-
in
41-
baseCfg
42-
// {
43-
backup = (baseCfg.backup or { }) // {
44-
snapshots = [ snapEntry ];
45-
};
46-
};
32+
# Resolve timer config: null → global default, else → override.
33+
effectiveTimerConfig = p: if p.timerConfig == null then cfg.timerConfig else p.timerConfig;
34+
35+
enabled = filterAttrs (_: p: p.enable) cfg.backups;
4736

48-
# Resolve timer config: null → global default, {} → disabled, else → override.
49-
effectiveTimerConfig =
50-
p:
51-
if p.timerConfig == null then
52-
cfg.timerConfig
53-
else if p.timerConfig == { } then
54-
null
55-
else
56-
p.timerConfig;
57-
58-
rusticBin = "${cfg.package}/bin/rustic";
37+
mkWrapper =
38+
name: p:
39+
pkgs.writeShellScriptBin "rustic-${name}" ''
40+
set -a
41+
${optionalString (p.environmentFile != null) ". ${p.environmentFile}"}
42+
set +a
43+
export RUSTIC_CACHE_DIR=/var/cache/rustic
44+
exec ${cfg.package}/bin/rustic -P ${name} "$@"
45+
'';
5946
in
6047
{
6148
options.services.rustic = {
@@ -69,21 +56,15 @@
6956
};
7057

7158
prometheus = {
72-
enable = mkEnableOption "prometheus metrics push to VictoriaMetrics";
59+
enable = mkEnableOption "rustic Prometheus metrics push";
7360
address = mkOption {
7461
type = types.str;
75-
default = "http://127.0.0.1:8428/api/v1/import/prometheus";
76-
description = "Prometheus remote write URL (VictoriaMetrics endpoint).";
62+
description = "Prometheus remote write URL";
7763
};
7864
user = mkOption {
7965
type = types.nullOr types.str;
8066
default = null;
81-
description = "Basic auth user for Prometheus Pushgateway (env RUSTIC_PROMETHEUS_USER).";
82-
};
83-
passwordFile = mkOption {
84-
type = types.nullOr types.path;
85-
default = null;
86-
description = "File containing basic auth password for Prometheus Pushgateway (env RUSTIC_PROMETHEUS_PASS).";
67+
description = "Basic auth user (env RUSTIC_PROMETHEUS_USER).";
8768
};
8869
};
8970

@@ -93,13 +74,9 @@
9374
OnCalendar = "daily";
9475
Persistent = true;
9576
};
96-
example = {
97-
OnCalendar = "0/4:00:00";
98-
Persistent = true;
99-
};
10077
description = ''
101-
Default systemd timer config. Used by profiles without their own timerConfig.
102-
Set profile's timerConfig to {} to disable timer (manual only).
78+
Default systemd timer config, e.g. { OnCalendar = "0/4:00:00"; Persistent = true; }.
79+
Used by profiles without their own timerConfig.
10380
'';
10481
};
10582

@@ -110,26 +87,10 @@
11087
options = {
11188
enable = mkEnableOption "this backup profile";
11289

113-
label = mkOption {
114-
type = types.nullOr types.str;
115-
default = null;
116-
example = "services";
117-
description = "Snapshot label. Falls back to profile attribute name.";
118-
};
119-
120-
sources = mkOption {
121-
type = types.listOf types.path;
122-
example = [ "/var/lib/9router" ];
123-
description = "Paths to back up.";
124-
};
125-
12690
environmentFile = mkOption {
12791
type = types.nullOr types.path;
12892
default = null;
129-
description = ''
130-
Path to environment file with RUSTIC_REPOSITORY, RUSTIC_PASSWORD,
131-
RUSTIC_REPOSITORY_OPTIONS_*. Usually a sops-decrypted dotenv file.
132-
'';
93+
description = "Path to environment file.";
13394
};
13495

13596
timerConfig = mkOption {
@@ -145,46 +106,46 @@
145106
'';
146107
};
147108

148-
# Free-form rustic TOML — each top-level key becomes a [section].
149-
# Use `snapshot = { ... }` for [[snapshot]] entries; sources auto-injected.
150109
settings = mkOption {
151110
type = types.attrsOf types.anything;
152111
default = { };
153112
example = {
154113
global.check-index = true;
155114
backup.skip-if-unchanged = true;
156115
backup."exclude-if-present" = [ ".nobackup" ];
116+
backup.snapshots = [
117+
{
118+
label = "services";
119+
sources = [ "/var/lib/9router" ];
120+
}
121+
];
157122
forget."keep-daily" = 14;
158123
forget."keep-weekly" = 8;
159124
forget."keep-monthly" = 24;
160-
snapshot = {
161-
label = "services";
162-
};
163125
};
164126
description = ''
165-
Full rustic TOML config. Each top-level attr = TOML section.
166-
Use `snapshot` for [[snapshot]] entries (sources auto-injected).
127+
Full rustic TOML config, written verbatim to /etc/rustic/<name>.toml.
167128
'';
168129
};
169130
};
170131
}
171132
)
172133
);
173134
default = { };
174-
description = "Attribute set of backup profiles. Key = profile name.";
135+
description = "Attribute set of backup profiles. Key is profile name.";
175136
};
176137
};
177138

178139
config = mkIf cfg.enable {
179-
environment.systemPackages = [ cfg.package ];
140+
environment.systemPackages = [ cfg.package ] ++ mapAttrsToList mkWrapper enabled;
180141

181142
environment.etc = mapAttrs' (
182143
name: p:
183144
nameValuePair "rustic/${name}.toml" {
184-
source = format.generate "rustic-${name}.toml" (genProfileConfig name p);
145+
source = format.generate "rustic-${name}.toml" p.settings;
185146
mode = "0440";
186147
}
187-
) (filterAttrs (_: p: p.enable) cfg.backups);
148+
) enabled;
188149

189150
systemd.services = mapAttrs' (
190151
name: p:
@@ -196,7 +157,7 @@
196157

197158
serviceConfig = {
198159
Type = "oneshot";
199-
ExecStart = "${rusticBin} backup -P ${name}";
160+
ExecStart = "${cfg.package}/bin/rustic backup -P ${name}";
200161
Environment = [
201162
"RUSTIC_CONFIG_DIR=/etc/rustic"
202163
"RUSTIC_CACHE_DIR=/var/cache/rustic"
@@ -205,12 +166,7 @@
205166
++ lib.optional (
206167
cfg.prometheus.enable && cfg.prometheus.user != null
207168
) "RUSTIC_PROMETHEUS_USER=${cfg.prometheus.user}";
208-
EnvironmentFile =
209-
lib.optionals (cfg.prometheus.enable && cfg.prometheus.passwordFile != null) [
210-
cfg.prometheus.passwordFile
211-
]
212-
++ lib.optional (p.environmentFile != null) p.environmentFile;
213-
ReadOnlyPaths = p.sources;
169+
EnvironmentFile = lib.optional (p.environmentFile != null) p.environmentFile;
214170
ReadWritePaths = [ "/var/cache/rustic" ];
215171
CacheDirectory = "rustic";
216172
NoNewPrivileges = true;

0 commit comments

Comments
 (0)