nixos/rustic: nixfmt

This commit is contained in:
Léo Gaspard
2025-01-11 15:32:08 +01:00
parent 59664286ac
commit 619f94bf5e
+223 -170
View File
@@ -1,46 +1,70 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
tomlFormat = pkgs.formats.toml {};
tomlFormat = pkgs.formats.toml { };
cfg = config.services.rustic;
configFiles = lib.mapAttrs (k: v: tomlFormat.generate "rustic-${k}.toml" v) cfg.profiles;
commonOpts = v:
if v ? files then v.files
else if v ? command then v.command
else if v ? postgres then v.postgres
else throw "unexpected: backup has none of the authorized types";
commonOpts =
v:
if v ? files then
v.files
else if v ? command then
v.command
else if v ? postgres then
v.postgres
else
throw "unexpected: backup has none of the authorized types";
backupType = v:
if v ? files then "files"
else if v ? command then "command"
else if v ? postgres then "postgres"
else throw "unexpected: backup has none of the authorized types";
backupType =
v:
if v ? files then
"files"
else if v ? command then
"command"
else if v ? postgres then
"postgres"
else
throw "unexpected: backup has none of the authorized types";
backupConfigToCmd = k: v: let
o = commonOpts v;
profileArgs = lib.concatMapStrings (s: " -P \"${s}\"") o.useProfiles;
extraArgs = lib.concatMapStrings (s: " \"${s}\"") o.extraArgs;
genericArgs = profileArgs + extraArgs;
backupConfigToCmd =
k: v:
let
o = commonOpts v;
profileArgs = lib.concatMapStrings (s: " -P \"${s}\"") o.useProfiles;
extraArgs = lib.concatMapStrings (s: " \"${s}\"") o.extraArgs;
genericArgs = profileArgs + extraArgs;
sourcesArgs = lib.concatMapStrings (s: " ${s}") v.files.sources;
asPathArg = lib.optionalString (v.files.asPath != null) " --as-path \"${v.files.asPath}\"";
ifFilesArgs = lib.optionalString (v ? files) (sourcesArgs + asPathArg);
sourcesArgs = lib.concatMapStrings (s: " ${s}") v.files.sources;
asPathArg = lib.optionalString (v.files.asPath != null) " --as-path \"${v.files.asPath}\"";
ifFilesArgs = lib.optionalString (v ? files) (sourcesArgs + asPathArg);
commandArg = " --stdin-command \"${v.command.command}\" -"; # stdin-command requires adding `-` as a source
stdinFilenameArg = lib.optionalString (v.command.filename != null) " --stdin-filename \"${v.command.filename}\"";
ifCommandArgs = lib.optionalString (v ? command) (commandArg + stdinFilenameArg);
commandArg = " --stdin-command \"${v.command.command}\" -"; # stdin-command requires adding `-` as a source
stdinFilenameArg = lib.optionalString (
v.command.filename != null
) " --stdin-filename \"${v.command.filename}\"";
ifCommandArgs = lib.optionalString (v ? command) (commandArg + stdinFilenameArg);
pipeJsonIntoCmd =
if o.pipeJsonInto ? nothing then ""
else if o.pipeJsonInto ? command then " --json | ${o.pipeJsonInto.command} \"$1\""
else if o.pipeJsonInto ? prometheus then " --json | ${prometheusWriteScript o.pipeJsonInto.prometheus.nodeExporterCollectorFolder} \"$1\""
else throw "unexpected: pipeJsonInto has none of the authorized types";
in
if v ? postgres
then
let systemctl = "${config.systemd.package}/bin/systemctl"; in
pipeJsonIntoCmd =
if o.pipeJsonInto ? nothing then
""
else if o.pipeJsonInto ? command then
" --json | ${o.pipeJsonInto.command} \"$1\""
else if o.pipeJsonInto ? prometheus then
" --json | ${prometheusWriteScript o.pipeJsonInto.prometheus.nodeExporterCollectorFolder} \"$1\""
else
throw "unexpected: pipeJsonInto has none of the authorized types";
in
if v ? postgres then
let
systemctl = "${config.systemd.package}/bin/systemctl";
in
pkgs.writeScript "rustic-postgres-starter-${k}" ''
#!${pkgs.bash}/bin/bash
set -euo pipefail
@@ -98,24 +122,29 @@ let
rustic_backup_${m}{id=\"{{id}}\"} {{${m}}}
'') prometheusAllMetrics;
prometheusWriteScript = f: pkgs.writeScript "rustic-write-to-prometheus" ''
#!${pkgs.bash}/bin/bash
set -euo pipefail
prometheusWriteScript =
f:
pkgs.writeScript "rustic-write-to-prometheus" ''
#!${pkgs.bash}/bin/bash
set -euo pipefail
${pkgs.jq}/bin/jq -r --arg template "${prometheusJqTemplate}" '
. as $json
| $template
| gsub("{{id}}"; "'"$1"'")
${lib.concatMapStrings
(m: ''| gsub("{{${m}}}"; $json.summary.${m} | tostring)'')
prometheusNumericMetrics}
${lib.concatMapStrings
# jq's `fromdate` builtins do not support sub-second accuracy (as of early 2025)
(m: ''| gsub("{{${m}}}"; $json.summary.${m} | sub(".[0-9]{9}Z"; "Z") | fromdateiso8601 | tostring)'')
prometheusTimeMetrics}
' > "${f}/rustic-backup-$1.prom.next"
mv "${f}/rustic-backup-$1.prom.next" "${f}/rustic-backup-$1.prom"
'';
${pkgs.jq}/bin/jq -r --arg template "${prometheusJqTemplate}" '
. as $json
| $template
| gsub("{{id}}"; "'"$1"'")
${lib.concatMapStrings (
m: ''| gsub("{{${m}}}"; $json.summary.${m} | tostring)''
) prometheusNumericMetrics}
${lib.concatMapStrings
# jq's `fromdate` builtins do not support sub-second accuracy (as of early 2025)
(
m: ''| gsub("{{${m}}}"; $json.summary.${m} | sub(".[0-9]{9}Z"; "Z") | fromdateiso8601 | tostring)''
)
prometheusTimeMetrics
}
' > "${f}/rustic-backup-$1.prom.next"
mv "${f}/rustic-backup-$1.prom.next" "${f}/rustic-backup-$1.prom"
'';
commonOptions = {
startAt = lib.mkOption {
@@ -129,13 +158,13 @@ let
useProfiles = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
default = [ ];
description = "Config profiles to use";
};
extraArgs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
default = [ ];
description = "Extra command-line arguments to pass to the `rustic` call.";
};
};
@@ -143,7 +172,9 @@ let
commonBackupOptions = commonOptions // {
pipeJsonInto = lib.mkOption {
description = "Command that will be called on the JSON result of `rustic backup`.";
default = { nothing = {}; };
default = {
nothing = { };
};
type = lib.types.attrTag {
nothing = lib.mkOption {
description = "Do nothing with the result.";
@@ -181,7 +212,7 @@ in
{
options.services.rustic = {
enable = lib.mkEnableOption "rustic";
package = lib.mkPackageOption pkgs "rustic" {};
package = lib.mkPackageOption pkgs "rustic" { };
checkProfiles = lib.mkOption {
type = lib.types.bool;
@@ -197,7 +228,7 @@ in
profiles = lib.mkOption {
type = lib.types.attrsOf tomlFormat.type;
default = {};
default = { };
description = ''
Configuration files for rustic, see
<https://github.com/rustic-rs/rustic/blob/main/config/README.md>
@@ -215,7 +246,7 @@ in
};
backups = lib.mkOption {
default = {};
default = { };
description = ''
Backup configurations.
@@ -224,76 +255,78 @@ in
running at different intervals, and backing up different folders or
databases.
'';
type = lib.types.attrsOf (lib.types.attrTag {
files = lib.mkOption {
description = "Backup files and directories.";
type = lib.types.submodule {
options = commonBackupOptions // {
sources = lib.mkOption {
type = lib.types.listOf lib.types.path;
default = [];
description = ''
List of sources to backup.
type = lib.types.attrsOf (
lib.types.attrTag {
files = lib.mkOption {
description = "Backup files and directories.";
type = lib.types.submodule {
options = commonBackupOptions // {
sources = lib.mkOption {
type = lib.types.listOf lib.types.path;
default = [ ];
description = ''
List of sources to backup.
Defaults to the ones defined in the relevant configuration files.
'';
};
Defaults to the ones defined in the relevant configuration files.
'';
};
asPath = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Manually set the backup path in snapshot";
asPath = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Manually set the backup path in snapshot";
};
};
};
};
};
command = lib.mkOption {
description = "Backup the output of a command.";
type = lib.types.submodule {
options = commonBackupOptions // {
command = lib.mkOption {
type = lib.types.str;
description = "Command of which to backup the output";
};
command = lib.mkOption {
description = "Backup the output of a command.";
type = lib.types.submodule {
options = commonBackupOptions // {
command = lib.mkOption {
type = lib.types.str;
description = "Command of which to backup the output";
};
filename = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
Filename to use in the backup.
filename = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = ''
Filename to use in the backup.
Defaults to the profile-provided filename, or `stdin`.
'';
Defaults to the profile-provided filename, or `stdin`.
'';
};
};
};
};
};
postgres = lib.mkOption {
description = ''
Backup all postgresql databases.
postgres = lib.mkOption {
description = ''
Backup all postgresql databases.
Use a custom `command` using `pg_dump` if you want to backup a single
database.
Use a custom `command` using `pg_dump` if you want to backup a single
database.
This backs up globals and individual databases in independent files.
'';
type = lib.types.submodule {
options = commonBackupOptions // {
prefix = lib.mkOption {
type = lib.types.str;
default = "/postgres";
description = "Path prefix for the dumps.";
This backs up globals and individual databases in independent files.
'';
type = lib.types.submodule {
options = commonBackupOptions // {
prefix = lib.mkOption {
type = lib.types.str;
default = "/postgres";
description = "Path prefix for the dumps.";
};
};
};
};
};
});
}
);
};
checks = lib.mkOption {
default = {};
default = { };
description = ''
Check configurations.
@@ -311,14 +344,16 @@ in
};
config = lib.mkIf cfg.enable {
system.checks = lib.optional cfg.checkProfiles (pkgs.runCommand "rustic-profiles-check" {} ''
${lib.concatMapStrings (p: ''
ln -s ${p} ./rustic-config-for-checking.toml
${cfg.package}/bin/rustic show-config -P rustic-config-for-checking > /dev/null
rm ./rustic-config-for-checking.toml
'') (lib.attrValues configFiles)}
touch $out
'');
system.checks = lib.optional cfg.checkProfiles (
pkgs.runCommand "rustic-profiles-check" { } ''
${lib.concatMapStrings (p: ''
ln -s ${p} ./rustic-config-for-checking.toml
${cfg.package}/bin/rustic show-config -P rustic-config-for-checking > /dev/null
rm ./rustic-config-for-checking.toml
'') (lib.attrValues configFiles)}
touch $out
''
);
environment.systemPackages = [ cfg.package ];
@@ -327,65 +362,83 @@ in
value.source = v;
}) configFiles;
systemd.services = lib.concatMapAttrs (k: v: {
"rustic-backup-${k}" = {
serviceConfig = {
Type = "oneshot";
User = "root";
ExecStart = "${backupConfigToCmd k v} ${backupType v}-${k}";
};
startAt = (commonOpts v).startAt;
};
} // lib.optionalAttrs (v ? postgres) {
"rustic-postgres-globals-${k}" = {
serviceConfig = {
Type = "oneshot";
User = "root";
ExecStart = "${backupConfigToCmd k {
command = v.postgres // {
filename = "${v.postgres.prefix}/globals.sql";
command = "${pkgs.sudo}/bin/sudo -u postgres ${config.services.postgresql.package}/bin/pg_dumpall --globals-only";
systemd.services =
lib.concatMapAttrs (
k: v:
{
"rustic-backup-${k}" = {
serviceConfig = {
Type = "oneshot";
User = "root";
ExecStart = "${backupConfigToCmd k v} ${backupType v}-${k}";
};
}} postgres-globals-${k}";
};
};
startAt = (commonOpts v).startAt;
};
}
// lib.optionalAttrs (v ? postgres) {
"rustic-postgres-globals-${k}" = {
serviceConfig = {
Type = "oneshot";
User = "root";
ExecStart = "${
backupConfigToCmd k {
command = v.postgres // {
filename = "${v.postgres.prefix}/globals.sql";
command = "${pkgs.sudo}/bin/sudo -u postgres ${config.services.postgresql.package}/bin/pg_dumpall --globals-only";
};
}
} postgres-globals-${k}";
};
};
"rustic-postgres-db-${k}@" = {
serviceConfig = {
Type = "oneshot";
User = "root";
ExecStart = "${backupConfigToCmd k {
command = v.postgres // {
filename = "$3";
command = "${pkgs.sudo}/bin/sudo -u postgres ${config.services.postgresql.package}/bin/pg_dump $2";
"rustic-postgres-db-${k}@" = {
serviceConfig = {
Type = "oneshot";
User = "root";
ExecStart = "${
backupConfigToCmd k {
command = v.postgres // {
filename = "$3";
command = "${pkgs.sudo}/bin/sudo -u postgres ${config.services.postgresql.package}/bin/pg_dump $2";
};
}
} 'postgres-db-${k}-%i' '%i' '${v.postgres.prefix}/db/%i.sql'";
};
}} 'postgres-db-${k}-%i' '%i' '${v.postgres.prefix}/db/%i.sql'";
};
};
}) cfg.backups // lib.concatMapAttrs (k: v: let
profiles = lib.concatMapStrings (p: " -P \"${p}\"") v.useProfiles;
extraArgs = lib.concatMapStrings (a: " \"${a}\"") v.extraArgs;
in {
"rustic-check-${k}" = {
serviceConfig = {
Type = "oneshot";
User = "root";
ExecStart = "${cfg.package}/bin/rustic check${profiles}${extraArgs}";
};
startAt = v.startAt;
};
}) cfg.checks // lib.optionalAttrs cfg.prune.enable (let
profiles = lib.concatMapStrings (p: " -P \"${p}\"") cfg.prune.useProfiles;
extraArgs = lib.concatMapStrings (a: " \"${a}\"") cfg.prune.extraArgs;
in {
"rustic-prune" = {
serviceConfig = {
Type = "oneshot";
User = "root";
ExecStart = "${cfg.package}/bin/rustic forget --prune${profiles}${extraArgs}";
};
startAt = cfg.prune.startAt;
};
});
};
}
) cfg.backups
// lib.concatMapAttrs (
k: v:
let
profiles = lib.concatMapStrings (p: " -P \"${p}\"") v.useProfiles;
extraArgs = lib.concatMapStrings (a: " \"${a}\"") v.extraArgs;
in
{
"rustic-check-${k}" = {
serviceConfig = {
Type = "oneshot";
User = "root";
ExecStart = "${cfg.package}/bin/rustic check${profiles}${extraArgs}";
};
startAt = v.startAt;
};
}
) cfg.checks
// lib.optionalAttrs cfg.prune.enable (
let
profiles = lib.concatMapStrings (p: " -P \"${p}\"") cfg.prune.useProfiles;
extraArgs = lib.concatMapStrings (a: " \"${a}\"") cfg.prune.extraArgs;
in
{
"rustic-prune" = {
serviceConfig = {
Type = "oneshot";
User = "root";
ExecStart = "${cfg.package}/bin/rustic forget --prune${profiles}${extraArgs}";
};
startAt = cfg.prune.startAt;
};
}
);
};
}