nixos/scrutiny: use genJqSecretsReplacementSnippet (#320672)

This commit is contained in:
Jon Seager
2025-03-21 08:42:37 +00:00
committed by GitHub
3 changed files with 115 additions and 79 deletions
+24 -3
View File
@@ -1,10 +1,11 @@
{ config, lib, pkgs, ... }:
{ config, lib, pkgs, utils, ... }:
let
inherit (lib) maintainers;
inherit (lib.meta) getExe;
inherit (lib.modules) mkIf mkMerge;
inherit (lib.options) literalExpression mkEnableOption mkOption mkPackageOption;
inherit (lib.types) bool enum nullOr port str submodule;
inherit (utils) genJqSecretsReplacementSnippet;
cfg = config.services.scrutiny;
# Define the settings format used for this program
@@ -36,6 +37,11 @@ in
Scrutiny settings to be rendered into the configuration file.
See <https://github.com/AnalogJ/scrutiny/blob/master/example.scrutiny.yaml>.
Options containing secret data should be set to an attribute set
containing the attribute `_secret`. This attribute should be a string
or structured JSON with `quote = false;`, pointing to a file that
contains the value the option should be set to.
'';
default = { };
type = submodule {
@@ -130,6 +136,11 @@ in
Collector settings to be rendered into the collector configuration file.
See <https://github.com/AnalogJ/scrutiny/blob/master/example.collector.yaml>.
Options containing secret data should be set to an attribute set
containing the attribute `_secret`. This attribute should be a string
or structured JSON with `quote = false;`, pointing to a file that
contains the value the option should be set to.
'';
default = { };
type = submodule {
@@ -177,6 +188,9 @@ in
SCRUTINY_WEB_DATABASE_LOCATION = "/var/lib/scrutiny/scrutiny.db";
SCRUTINY_WEB_SRC_FRONTEND_PATH = "${cfg.package}/share/scrutiny";
};
preStart = ''
${genJqSecretsReplacementSnippet cfg.settings "/run/scrutiny/config.yaml"}
'';
postStart = ''
for i in $(seq 300); do
if "${lib.getExe pkgs.curl}" --fail --silent --head "http://${cfg.settings.web.listen.host}:${toString cfg.settings.web.listen.port}" >/dev/null; then
@@ -191,8 +205,10 @@ in
'';
serviceConfig = {
DynamicUser = true;
ExecStart = "${getExe cfg.package} start --config ${settingsFormat.generate "scrutiny.yaml" cfg.settings}";
ExecStart = "${getExe cfg.package} start --config /run/scrutiny/config.yaml";
Restart = "always";
RuntimeDirectory = "scrutiny";
RuntimeDirectoryMode = "0700";
StateDirectory = "scrutiny";
StateDirectoryMode = "0750";
};
@@ -216,9 +232,14 @@ in
COLLECTOR_VERSION = "1";
COLLECTOR_API_ENDPOINT = cfg.collector.settings.api.endpoint;
};
preStart = ''
${genJqSecretsReplacementSnippet cfg.collector.settings "/run/scrutiny-collector/config.yaml"}
'';
serviceConfig = {
Type = "oneshot";
ExecStart = "${getExe cfg.collector.package} run --config ${settingsFormat.generate "scrutiny-collector.yaml" cfg.collector.settings}";
ExecStart = "${getExe cfg.collector.package} run --config /run/scrutiny-collector/config.yaml";
RuntimeDirectory = "scrutiny-collector";
RuntimeDirectoryMode = "0700";
};
startAt = cfg.collector.schedule;
};
+1 -1
View File
@@ -1051,7 +1051,7 @@ in {
scaphandre = handleTest ./scaphandre.nix {};
schleuder = handleTest ./schleuder.nix {};
scion-freestanding-deployment = handleTest ./scion/freestanding-deployment {};
scrutiny = handleTest ./scrutiny.nix {};
scrutiny = runTest ./scrutiny.nix;
sddm = handleTest ./sddm.nix {};
sdl3 = handleTest ./sdl3.nix { };
seafile = handleTest ./seafile.nix {};
+90 -75
View File
@@ -1,83 +1,98 @@
import ./make-test-python.nix (
{ lib, ... }:
{ lib, ... }:
{
name = "scrutiny";
meta.maintainers = with lib.maintainers; [ jnsgruk ];
{
name = "scrutiny";
meta.maintainers = with lib.maintainers; [ jnsgruk ];
nodes = {
machine =
{
self,
pkgs,
lib,
...
}:
{
services = {
scrutiny.enable = true;
scrutiny.collector.enable = true;
nodes = {
machine =
{
self,
pkgs,
lib,
...
}:
{
services = {
scrutiny = {
enable = true;
settings = {
notify.urls = [
{
_secret = pkgs.writeText "notify-script" "script://${pkgs.writeShellScript "touch-test-file" ''
echo "HelloWorld" > /run/scrutiny/hello
''}";
}
];
};
};
environment.systemPackages =
let
seleniumScript =
pkgs.writers.writePython3Bin "selenium-script"
{
libraries = with pkgs.python3Packages; [ selenium ];
}
''
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
options = Options()
options.add_argument("--headless")
service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}") # noqa: E501
driver = webdriver.Firefox(options=options, service=service)
driver.implicitly_wait(10)
driver.get("http://localhost:8080/web/dashboard")
wait = WebDriverWait(driver, 10).until(
EC.text_to_be_present_in_element(
(By.TAG_NAME, "body"), "Drive health at a glance")
)
body_text = driver.find_element(By.TAG_NAME, "body").text
assert "Temperature history for each device" in body_text
driver.close()
'';
in
with pkgs;
[
curl
firefox-unwrapped
geckodriver
seleniumScript
];
scrutiny.collector.enable = true;
};
};
# This is the test code that will check if our service is running correctly:
testScript = ''
start_all()
# Wait for Scrutiny to be available
machine.wait_for_unit("scrutiny")
machine.wait_for_open_port(8080)
environment.systemPackages =
let
seleniumScript =
pkgs.writers.writePython3Bin "selenium-script"
{
libraries = with pkgs.python3Packages; [ selenium ];
}
''
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# Ensure the API responds as we expect
output = machine.succeed("curl localhost:8080/api/health")
assert output == '{"success":true}'
options = Options()
options.add_argument("--headless")
service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}") # noqa: E501
# Start the collector service to send some metrics
collect = machine.succeed("systemctl start scrutiny-collector.service")
driver = webdriver.Firefox(options=options, service=service)
driver.implicitly_wait(10)
driver.get("http://localhost:8080/web/dashboard")
# Ensure the application is actually rendered by the Javascript
machine.succeed("PYTHONUNBUFFERED=1 selenium-script")
'';
}
)
wait = WebDriverWait(driver, 10).until(
EC.text_to_be_present_in_element(
(By.TAG_NAME, "body"), "Drive health at a glance")
)
body_text = driver.find_element(By.TAG_NAME, "body").text
assert "Temperature history for each device" in body_text
driver.close()
'';
in
with pkgs;
[
curl
firefox-unwrapped
geckodriver
seleniumScript
];
};
};
# This is the test code that will check if our service is running correctly:
testScript = ''
start_all()
# Wait for Scrutiny to be available
machine.wait_for_unit("scrutiny")
machine.wait_for_open_port(8080)
# Ensure the API responds as we expect
output = machine.succeed("curl localhost:8080/api/health")
assert output == '{"success":true}'
# Start the collector service to send some metrics
collect = machine.succeed("systemctl start scrutiny-collector.service")
# Ensure the application is actually rendered by the Javascript
machine.succeed("PYTHONUNBUFFERED=1 selenium-script")
# Test notification and genJqSecretsReplacementSnippet
machine.succeed("curl -X POST http://localhost:8080/api/health/notify")
machine.wait_for_file("/run/scrutiny/hello")
output = machine.succeed("cat /run/scrutiny/hello")
assert "HelloWorld" in output
'';
}