Merge pull request #270475 from MinerSebas/prometheus-restic-exporter

prometheus-restic-exporter: init at 1.4.0
This commit is contained in:
h7x4 2024-02-01 20:05:11 +01:00 committed by GitHub
commit 932344b54a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 226 additions and 1 deletions

View File

@ -12413,6 +12413,12 @@
githubId = 92937;
name = "Breland Miley";
};
minersebas = {
email = "scherthan_sebastian@web.de";
github = "MinerSebas";
githubId = 66798382;
name = "Sebastian Maximilian Scherthan";
};
minijackson = {
email = "minijackson@riseup.net";
github = "minijackson";

View File

@ -70,6 +70,7 @@ let
"pve"
"py-air-control"
"redis"
"restic"
"rspamd"
"rtl_433"
"sabnzbd"

View File

@ -0,0 +1,131 @@
{ config, lib, pkgs, options }:
with lib;
let
cfg = config.services.prometheus.exporters.restic;
in
{
port = 9753;
extraOpts = {
repository = mkOption {
type = types.str;
description = lib.mdDoc ''
URI pointing to the repository to monitor.
'';
example = "sftp:backup@192.168.1.100:/backups/example";
};
passwordFile = mkOption {
type = types.path;
description = lib.mdDoc ''
File containing the password to the repository.
'';
example = "/etc/nixos/restic-password";
};
environmentFile = mkOption {
type = with types; nullOr path;
default = null;
description = lib.mdDoc ''
File containing the credentials to access the repository, in the
format of an EnvironmentFile as described by systemd.exec(5)
'';
};
refreshInterval = mkOption {
type = types.ints.unsigned;
default = 60;
description = lib.mdDoc ''
Refresh interval for the metrics in seconds.
Computing the metrics is an expensive task, keep this value as high as possible.
'';
};
rcloneOptions = mkOption {
type = with types; attrsOf (oneOf [ str bool ]);
default = { };
description = lib.mdDoc ''
Options to pass to rclone to control its behavior.
See <https://rclone.org/docs/#options> for
available options. When specifying option names, strip the
leading `--`. To set a flag such as
`--drive-use-trash`, which does not take a value,
set the value to the Boolean `true`.
'';
};
rcloneConfig = mkOption {
type = with types; attrsOf (oneOf [ str bool ]);
default = { };
description = lib.mdDoc ''
Configuration for the rclone remote being used for backup.
See the remote's specific options under rclone's docs at
<https://rclone.org/docs/>. When specifying
option names, use the "config" name specified in the docs.
For example, to set `--b2-hard-delete` for a B2
remote, use `hard_delete = true` in the
attribute set.
::: {.warning}
Secrets set in here will be world-readable in the Nix
store! Consider using the {option}`rcloneConfigFile`
option instead to specify secret values separately. Note that
options set here will override those set in the config file.
:::
'';
};
rcloneConfigFile = mkOption {
type = with types; nullOr path;
default = null;
description = lib.mdDoc ''
Path to the file containing rclone configuration. This file
must contain configuration for the remote specified in this backup
set and also must be readable by root.
::: {.caution}
Options set in `rcloneConfig` will override those set in this
file.
:::
'';
};
};
serviceOpts = {
serviceConfig = {
ExecStart = ''
${pkgs.prometheus-restic-exporter}/bin/restic-exporter.py \
${concatStringsSep " \\\n " cfg.extraFlags}
'';
EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile;
};
environment =
let
rcloneRemoteName = builtins.elemAt (splitString ":" cfg.repository) 1;
rcloneAttrToOpt = v: "RCLONE_" + toUpper (builtins.replaceStrings [ "-" ] [ "_" ] v);
rcloneAttrToConf = v: "RCLONE_CONFIG_" + toUpper (rcloneRemoteName + "_" + v);
toRcloneVal = v: if lib.isBool v then lib.boolToString v else v;
in
{
RESTIC_REPO_URL = cfg.repository;
RESTIC_REPO_PASSWORD_FILE = cfg.passwordFile;
LISTEN_ADDRESS = cfg.listenAddress;
LISTEN_PORT = toString cfg.port;
REFRESH_INTERVAL = toString cfg.refreshInterval;
}
// (mapAttrs'
(name: value:
nameValuePair (rcloneAttrToOpt name) (toRcloneVal value)
)
cfg.rcloneOptions)
// optionalAttrs (cfg.rcloneConfigFile != null) {
RCLONE_CONFIG = cfg.rcloneConfigFile;
}
// (mapAttrs'
(name: value:
nameValuePair (rcloneAttrToConf name) (toRcloneVal value)
)
cfg.rcloneConfig);
};
}

View File

@ -1177,6 +1177,39 @@ let
'';
};
restic =
let
repository = "rest:http://127.0.0.1:8000";
passwordFile = pkgs.writeText "restic-test-password" "test-password";
in
{
exporterConfig = {
enable = true;
inherit repository passwordFile;
};
metricProvider = {
services.restic.server = {
enable = true;
extraFlags = [ "--no-auth" ];
};
environment.systemPackages = [ pkgs.restic ];
};
exporterTest = ''
# prometheus-restic-exporter.service fails without initialised repository
systemctl("stop prometheus-restic-exporter.service")
# Initialise the repository
wait_for_unit("restic-rest-server.service")
wait_for_open_port(8000)
succeed("restic init --repo ${repository} --password-file ${passwordFile}")
systemctl("start prometheus-restic-exporter.service")
wait_for_unit("prometheus-restic-exporter.service")
wait_for_open_port(9753)
wait_until_succeeds("curl -sSf localhost:9753/metrics | grep 'restic_check_success 1.0'")
'';
};
rspamd = {
exporterConfig = {
enable = true;
@ -1684,7 +1717,12 @@ mapAttrs
testScript = ''
${nodeName}.start()
${concatStringsSep "\n" (map (line:
if (builtins.substring 0 1 line == " " || builtins.substring 0 1 line == ")")
if builtins.any (b: b) [
(builtins.match "^[[:space:]]*$" line != null)
(builtins.substring 0 1 line == "#")
(builtins.substring 0 1 line == " ")
(builtins.substring 0 1 line == ")")
]
then line
else "${nodeName}.${line}"
) (splitString "\n" (removeSuffix "\n" testConfig.exporterTest)))}

View File

@ -0,0 +1,49 @@
{ lib
, stdenvNoCC
, fetchFromGitHub
, python3
, restic
, nixosTests
}:
stdenvNoCC.mkDerivation rec {
pname = "prometheus-restic-exporter";
version = "1.4.0";
src = fetchFromGitHub {
owner = "ngosang";
repo = "restic-exporter";
rev = version;
hash = "sha256-Qwhlecginl5+V+iddN/vIHfJA1kQOZtscECsoD4LJPE=";
};
buildInputs = [
(python3.withPackages (ps: [ ps.prometheus-client ]))
];
installPhase = ''
runHook preInstall
install -D -m0755 restic-exporter.py $out/bin/restic-exporter.py
substituteInPlace $out/bin/restic-exporter.py --replace \"restic\" \"${lib.makeBinPath [ restic ]}/restic\"
patchShebangs $out/bin/restic-exporter.py
runHook postInstall
'';
passthru.tests = {
restic-exporter = nixosTests.prometheus-exporters.restic;
};
meta = with lib; {
description = "Prometheus exporter for the Restic backup system";
homepage = "https://github.com/ngosang/restic-exporter";
changelog = "https://github.com/ngosang/restic-exporter/blob/${src.rev}/CHANGELOG.md";
license = licenses.mit;
maintainers = with maintainers; [ minersebas ];
mainProgram = "restic-exporter.py";
platforms = platforms.all;
};
}