summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nixos/helpers.nix11
-rw-r--r--nixos/hosts/tente/default.nix7
-rw-r--r--nixos/hosts/tente/matrix.nix39
-rw-r--r--nixos/hosts/tente/monitoring.nix287
-rw-r--r--nixos/npins/sources.json34
-rw-r--r--nixos/secrets/grafana-matrix-forwarder-env.agebin0 -> 449 bytes
-rw-r--r--nixos/secrets/secrets.nix2
-rw-r--r--nixos/shared/alloy-nix-config/alloy_nix_config.go129
-rw-r--r--nixos/shared/alloy-nix-config/default.nix8
-rw-r--r--nixos/shared/alloy-nix-config/go.mod3
-rw-r--r--nixos/shared/grafana-matrix-forwarder/default.nix10
-rw-r--r--nixos/shared/grafana-matrix-forwarder/service.nix31
-rw-r--r--nixos/shared/prometheus-sql-exporter/default.nix20
-rw-r--r--nixos/shared/prometheus-sql-exporter/service.nix13
14 files changed, 401 insertions, 193 deletions
diff --git a/nixos/helpers.nix b/nixos/helpers.nix
index cadf230..f8f1384 100644
--- a/nixos/helpers.nix
+++ b/nixos/helpers.nix
@@ -60,6 +60,17 @@
'}';
'';
+ writeAlloyConfig = cfg:
+ let
+ alloy-nix-config = "${pkgs.callPackage <top/shared/alloy-nix-config> {}}/bin/alloy-nix-config";
+ in
+ pkgs.runCommand "generated-config" {} ''
+ ${alloy-nix-config} ${pkgs.writeText "input.json" (builtins.toJSON cfg)} $out
+ # FUTURE: run alloy validate
+ '';
+
+ alloyConfigRef = s: { "$ref" = s; };
+
joinWgNamespace = ns: cfg:
lib.attrsets.recursiveUpdate cfg {
bindsTo = ["netns@${ns}.service"];
diff --git a/nixos/hosts/tente/default.nix b/nixos/hosts/tente/default.nix
index ac8f438..f03156b 100644
--- a/nixos/hosts/tente/default.nix
+++ b/nixos/hosts/tente/default.nix
@@ -9,8 +9,10 @@ let
acmeEmail = "martin@push-f.com";
sources = import <top/npins>;
helpers = import <top/helpers.nix> { inherit config lib pkgs; };
+ pkgs-unstable = import sources.nixpkgs-unstable {};
in
-{
+rec {
+ _module.args = { inherit pkgs-unstable; };
imports = [
./hardware-configuration.nix
<top/profiles/server>
@@ -116,6 +118,7 @@ in
monitoring.grafanaUiPort = 3000;
monitoring.alloyUiPort = 3001;
+ monitoring.grafanaMatrixForwarderPort = 3002;
monitoring.lokiPort = 3030;
gotify.port = 4000;
monitoring.prometheusNodeExporterPort = 9002;
@@ -123,6 +126,8 @@ in
headscale.port = 8080;
matrix.port = 8008;
+ monitoring.matrixServerUrl = "http://localhost:${toString matrix.port}";
+
# Use the GRUB 2 boot loader.
boot.loader.grub.enable = true;
# boot.loader.grub.efiSupport = true;
diff --git a/nixos/hosts/tente/matrix.nix b/nixos/hosts/tente/matrix.nix
index 2eb8673..89782fc 100644
--- a/nixos/hosts/tente/matrix.nix
+++ b/nixos/hosts/tente/matrix.nix
@@ -1,4 +1,4 @@
-{ config, lib, pkgs, ... }:
+{ config, lib, pkgs, pkgs-unstable, ... }:
let
cfg = config.matrix;
@@ -19,32 +19,19 @@ in
config = {
services = {
- matrix-synapse = {
+ matrix-conduit = {
enable = true;
+ package = pkgs-unstable.matrix-conduit;
settings = {
- server_name = cfg.serverName;
- listeners = [{
- # This listener matches the default of NixOS 24.11 (replicated here to make the port configurable).
- bind_addresses = ["127.0.0.1"];
+ global = {
+ server_name = cfg.serverName;
port = cfg.port;
- resources = [
- {
- compress = true;
- names = ["client"];
- }
- {
- compress = false;
- names = ["federation"];
- }
- ];
- tls = false;
- type = "http";
- x_forwarded = true;
- }];
+ address = "127.0.0.1"; # this is the default of conduit but the nixos service defaults to ::1
+ database_backend = "rocksdb";
+ enable_lightning_bolt = false;
+ allow_registration = false;
+ };
};
-
- # The default is INFO which can easily spam the systemd journal with 500k messages a day.
- log.root.level = "WARNING";
};
nginx.virtualHosts.${cfg.apiDomain} = {
@@ -54,14 +41,12 @@ in
# TODO: add locations."/" with some message
- # Forward all Matrix API calls to the synapse Matrix homeserver. A trailing slash
+ # Forward all Matrix API calls to the Conduit Matrix homeserver. A trailing slash
# *must not* be used here.
locations."/_matrix".proxyPass = "http://127.0.0.1:${toString cfg.port}";
- # Forward requests for e.g. SSO and password-resets.
- locations."/_synapse/client".proxyPass = "http://127.0.0.1:${toString cfg.port}";
};
- # I don't really care about these nginx access logs. Synapse has its own
+ # I don't really care about these nginx access logs. Conduit has its own
# log anyway and with the default log rotation (weekly and delaycompress=true)
# the access logs from last week took up ~800MB.
logrotate.settings.matrix-nginx-access-log =
diff --git a/nixos/hosts/tente/monitoring.nix b/nixos/hosts/tente/monitoring.nix
index 545ae24..f6ed7cf 100644
--- a/nixos/hosts/tente/monitoring.nix
+++ b/nixos/hosts/tente/monitoring.nix
@@ -2,12 +2,16 @@
let
cfg = config.monitoring;
+ helpers = import <top/helpers.nix> { inherit config lib pkgs; };
in
{
options.monitoring = {
grafanaUiPort = lib.mkOption {
type = lib.types.int;
};
+ grafanaMatrixForwarderPort = lib.mkOption {
+ type = lib.types.int;
+ };
lokiPort = lib.mkOption {
type = lib.types.int;
};
@@ -20,13 +24,19 @@ in
prometheusSqlExporterPort = lib.mkOption {
type = lib.types.int;
};
+ matrixServerUrl = lib.mkOption {
+ type = lib.types.str;
+ };
};
imports = [
<top/shared/prometheus-sql-exporter/service.nix>
+ <top/shared/grafana-matrix-forwarder/service.nix>
];
config = {
+ age.secrets.grafana-matrix-forwarder-env.file = <top/secrets/grafana-matrix-forwarder-env.age>;
+
services.grafana = {
enable = true;
settings = {
@@ -53,6 +63,12 @@ in
];
};
};
+ services.grafana-matrix-forwarder = {
+ enable = true;
+ port = cfg.grafanaMatrixForwarderPort;
+ homeserver = cfg.matrixServerUrl;
+ environmentFile = config.age.secrets.grafana-matrix-forwarder-env.path;
+ };
services.prometheus = {
enable = true;
@@ -166,159 +182,134 @@ in
services.alloy = {
enable = true;
extraFlags = ["--server.http.listen-addr=0.0.0.0:${toString cfg.alloyUiPort}"];
- # TODO: submit PR to nixpkgs so that the alloy config can be specified as a JSON expression
- configPath = pkgs.writeText "config.alloy" ''
- loki.source.journal "journal" {
- max_age = "12h0m0s"
- relabel_rules = discovery.relabel.journal.rules
- forward_to = [loki.process.journal.receiver]
- labels = {
- host = "tente",
- job = "systemd-journal",
- }
- }
-
- loki.process "journal" {
- forward_to = [loki.write.default.receiver]
-
- stage.match {
- // Select messages from systemd services that have LogExtraFields=LOG_FORMAT=logfmt.
- selector = "{__journal_LOG_FORMAT=\"logfmt\"}"
- stage.logfmt {
- mapping = { time = "", level = "" }
- }
- stage.timestamp {
- source = "time"
- format = "RFC3339"
- }
- stage.template {
- // The slog package of the Go standard library prints levels as uppercase.
- source = "level"
- template = "{{ ToLower .Value }}"
- }
- stage.structured_metadata {
- values = { level = "" }
- }
- }
- }
-
- discovery.relabel "journal" {
- targets = []
-
- rule {
- source_labels = ["__journal__systemd_unit"]
- target_label = "unit"
- }
- }
-
- loki.source.file "nginx_access" {
- targets = local.file_match.nginx_access.targets
- forward_to = [loki.process.nginx_access.receiver]
- }
-
- local.file_match "nginx_access" {
- path_targets = [{
- __path__ = "/var/log/nginx/*.access.log",
- }]
- }
-
- loki.process "nginx_access" {
- forward_to = [loki.write.default.receiver]
-
- stage.static_labels {
- values = {
- job = "nginx",
- }
- }
-
- // Extracting the log file name as vhost because it's more convenient
- // to query for than the full filename. We could also use server_name
- // but there could be wildcard server_names and Loki labels should have
- // a low cardinality for performance reasons.
- stage.regex {
- source = "filename"
- expression = "(?P<vhost>[^/]+)\\.access\\.log$"
- }
-
- stage.labels {
- values = {
- vhost = "",
- }
- }
-
- stage.json {
- expressions = { "msec" = "", path = "" }
- }
-
- stage.timestamp {
- source = "msec"
- format = "Unix"
- }
-
- // Setting level=info to prevent Loki's log level detection from wrongly
- // detecting messages with paths containing "error" as errors.
- // Creating the filetype entry via stage.template because there's no
- // static_structured_metadata stage yet. (https://github.com/grafana/loki/issues/16703)
- stage.template {
- source = "level"
- template = "info"
- }
- stage.structured_metadata {
- values = { level = "" }
- }
-
- stage.labels {
- values = {
- // Temporarily adding path as a label so that we can use it in the match selectors.
- path = "",
- }
- }
-
- stage.match {
- selector = "{path=~\"/\\\\.well-known/.*\"}"
- // Creating the filetype entry via stage.template because there's no
- // static_structured_metadata stage yet. (https://github.com/grafana/loki/issues/16703)
- stage.template {
- source = "filetype"
- template = "well-known"
- }
- }
+ configPath =
+ let
+ ref = helpers.alloyConfigRef;
+ in
+ helpers.writeAlloyConfig {
+ "loki.source.journal".journal = {
+ max_age = "12h0m0s";
+ relabel_rules = ref "discovery.relabel.journal.rules";
+ forward_to = [(ref "loki.process.journal.receiver")];
+ labels = {
+ host = "tente";
+ job = "systemd-journal";
+ };
+ };
+ "loki.process".journal = {
+ forward_to = [(ref "loki.write.default.receiver")];
+ blocks = [
+ {
+ name = "stage.match";
+ # Select messages from systemd services that have LogExtraFields=LOG_FORMAT=logfmt.
+ selector = ''{__journal_LOG_FORMAT="logfmt"}'';
+ blocks = [
+ { name = "stage.logfmt"; mapping = { time = ""; level = ""; }; }
+ { name = "stage.timestamp"; source = "time"; format = "RFC3339"; }
+ {
+ # The slog package of the Go standard library prints levels as uppercase.
+ name = "stage.template";
+ source = "level";
+ template = "{{ ToLower .Value }}";
+ }
+ { name = "stage.structured_metadata"; values = { level = ""; }; }
+ ];
+ }
+ ];
+ };
+ "discovery.relabel".journal = {
+ targets = [];
+ blocks = [
+ {
+ name = "rule";
+ source_labels = ["__journal__systemd_unit"];
+ target_label = "unit";
+ }
+ ];
+ };
- stage.match {
- selector = "{path=\"/robots.txt\"}"
- stage.template {
- source = "filetype"
- template = "robots.txt"
- }
- }
+ "loki.source.file".nginx_access = {
+ targets = ref "local.file_match.nginx_access.targets";
+ forward_to = [(ref "loki.process.nginx_access.receiver")];
+ };
+ "local.file_match".nginx_access = {
+ path_targets = [{
+ __path__ = "/var/log/nginx/*.access.log";
+ }];
+ };
+ "loki.process".nginx_access = {
+ forward_to = [(ref "loki.write.default.receiver")];
+ blocks = [
+ { name = "stage.static_labels"; values = { job = "nginx"; }; }
- stage.match {
- selector = "{path=~\".*\\\\.atom$\"}"
- stage.template {
- source = "filetype"
- template = "feed"
- }
- }
+ {
+ # Extracting the log file name as vhost because it's more convenient
+ # to query for than the full filename. We could also use server_name
+ # but there could be wildcard server_names and Loki labels should have
+ # a low cardinality for performance reasons.
+ name = "stage.regex";
+ source = "filename";
+ expression = "(?P<vhost>[^/]+)\\.access\\.log$";
+ }
- stage.structured_metadata {
- values = {
- filetype = "",
- }
- }
+ { name = "stage.labels"; values = { vhost = ""; }; }
+ { name = "stage.json"; expressions = { msec = ""; path = ""; }; }
+ { name = "stage.timestamp"; source = "msec"; format = "Unix"; }
+ {
+ # Setting level=info to prevent Loki's log level detection from wrongly
+ # detecting messages with paths containing "error" as errors.
+ # Creating the filetype entry via stage.template because there's no
+ # static_structured_metadata stage yet. (https://github.com/grafana/loki/issues/16703)
+ name = "stage.template";
+ source = "level";
+ template = "info";
+ }
+ { name = "stage.structured_metadata"; values = { level = ""; }; }
- // Dropping path again because it has a too high cardinality for a label.
- stage.label_drop {
- values = [ "path" ]
- }
- }
+ # Temporarily adding path as a label so that we can use it in the match selectors.
+ { name = "stage.labels"; values = { path = ""; }; }
+ {
+ name = "stage.match";
+ selector = "{path=~\"/\\\\.well-known/.*\"}";
+ # Creating the filetype entry via stage.template because there's no
+ # static_structured_metadata stage yet. (https://github.com/grafana/loki/issues/16703)
+ blocks = [
+ { name = "stage.template"; source = "filetype"; template = "well-known"; }
+ ];
+ }
+ {
+ name = "stage.match";
+ selector = "{path=\"/robots.txt\"}";
+ blocks = [
+ { name = "stage.template"; source = "filetype"; template = "robots.txt"; }
+ ];
+ }
+ {
+ name = "stage.match";
+ selector = "{path=~\".*\\\\.atom$\"}";
+ blocks = [
+ { name = "stage.template"; source = "filetype"; template = "feed"; }
+ ];
+ }
+ {
+ name = "stage.structured_metadata";
+ values = { filetype = ""; };
+ }
- loki.write "default" {
- endpoint {
- url = "http://127.0.0.1:${toString cfg.lokiPort}/loki/api/v1/push"
- }
- external_labels = {}
- }
- '';
+ # Dropping path again because it has a too high cardinality for a label.
+ { name = "stage.label_drop"; values = ["path"]; }
+ ];
+ };
+ "loki.write".default = {
+ blocks = [
+ {
+ name = "endpoint";
+ url = "http://127.0.0.1:${toString cfg.lokiPort}/loki/api/v1/push";
+ }
+ ];
+ external_labels = {};
+ };
+ };
};
};
}
diff --git a/nixos/npins/sources.json b/nixos/npins/sources.json
index 826b2a9..6f86eaa 100644
--- a/nixos/npins/sources.json
+++ b/nixos/npins/sources.json
@@ -16,6 +16,21 @@
"url": "https://api.github.com/repos/ryantm/agenix/tarball/0.15.0",
"hash": "01dhrghwa7zw93cybvx4gnrskqk97b004nfxgsys0736823956la"
},
+ "grafana-matrix-forwarder": {
+ "type": "GitRelease",
+ "repository": {
+ "type": "Git",
+ "url": "https://gitlab.com/hctrdev/grafana-matrix-forwarder.git"
+ },
+ "pre_releases": false,
+ "version_upper_bound": null,
+ "release_prefix": null,
+ "submodules": false,
+ "version": "v0.8.3",
+ "revision": "a77a360503ea12c8244646753a3bf271428ddd02",
+ "url": null,
+ "hash": "1mdgv6jfbjs3xsqk9dxli6w345jws8j5qgzqf6wljmxlvibbic6y"
+ },
"my-geopos-link": {
"type": "Git",
"repository": {
@@ -103,8 +118,23 @@
"nixpkgs-unstable": {
"type": "Channel",
"name": "nixpkgs-unstable",
- "url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.11pre832020.6b4955211758/nixexprs.tar.xz",
- "hash": "128piyf6sw0r07bxhix6i998h30zqqgaz1sknzrgy6yvh8jgawaz"
+ "url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.11pre852399.4c202d26483c/nixexprs.tar.xz",
+ "hash": "0aaj59kn5q6n5cwq3i2a9sa89z0q71cy69kmpiy0kkn53hv3dxch"
+ },
+ "prometheus-sql-exporter": {
+ "type": "GitRelease",
+ "repository": {
+ "type": "Git",
+ "url": "https://github.com/burningalchemist/sql_exporter"
+ },
+ "pre_releases": false,
+ "version_upper_bound": null,
+ "release_prefix": null,
+ "submodules": false,
+ "version": "0.18.1",
+ "revision": "6e73ff71a8976939c28ce9a0ddbeefe0801d0a37",
+ "url": null,
+ "hash": "1a3j3rsjvw6wqyayml5x5c7x18bvn45ldr2p280yn9pqdzihk480"
}
},
"version": 5
diff --git a/nixos/secrets/grafana-matrix-forwarder-env.age b/nixos/secrets/grafana-matrix-forwarder-env.age
new file mode 100644
index 0000000..84cab14
--- /dev/null
+++ b/nixos/secrets/grafana-matrix-forwarder-env.age
Binary files differ
diff --git a/nixos/secrets/secrets.nix b/nixos/secrets/secrets.nix
index 6021803..282ee6c 100644
--- a/nixos/secrets/secrets.nix
+++ b/nixos/secrets/secrets.nix
@@ -3,7 +3,9 @@ let
hamac = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJmjbC0gk2s/qDQ+QR//GJH0ZPld99L0EtX7dPP5h2RN";
ev = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINCSypbTOnAYBO32vUUieOsb6ws32gCsDg8nB8JhuFuI";
+ tente = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGTyZdGXxkAjzRulRFB23AX0T/lVLZOaSaFJi+thsjgx";
in
{
"vpn-se-privKey.age".publicKeys = [ martin hamac ev ];
+ "grafana-matrix-forwarder-env.age".publicKeys = [ martin tente ];
}
diff --git a/nixos/shared/alloy-nix-config/alloy_nix_config.go b/nixos/shared/alloy-nix-config/alloy_nix_config.go
new file mode 100644
index 0000000..4b6eb63
--- /dev/null
+++ b/nixos/shared/alloy-nix-config/alloy_nix_config.go
@@ -0,0 +1,129 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "maps"
+ "os"
+ "slices"
+ "strconv"
+ "strings"
+)
+
+func main() {
+ if len(os.Args) != 3 {
+ fmt.Fprintf(os.Stderr, "usage: %s <json_path> <out_path>\n", os.Args[0])
+ os.Exit(1)
+ }
+
+ jsonPath := os.Args[1]
+ outPath := os.Args[2]
+
+ jsonData, err := os.ReadFile(jsonPath)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error reading file %s: %v\n", jsonPath, err)
+ os.Exit(1)
+ }
+
+ // It would be nice to preserve the order of blocks ... except that we can't
+ // because Nix already doesn't preserve the order of attribute sets.
+ var config map[string]any
+ if err := json.Unmarshal(jsonData, &config); err != nil {
+ fmt.Fprintf(os.Stderr, "error parsing JSON: %v\n", err)
+ os.Exit(1)
+ }
+
+ result := formatConfig(config)
+
+ if err := os.WriteFile(outPath, []byte(result), 0644); err != nil {
+ fmt.Fprintf(os.Stderr, "error writing file %s: %v\n", outPath, err)
+ os.Exit(1)
+ }
+}
+
+func formatConfig(config map[string]any) string {
+ var s strings.Builder
+
+ for _, blockName := range slices.Sorted(maps.Keys(config)) {
+ labels := config[blockName]
+
+ if labelsMap, ok := labels.(map[string]any); ok {
+ for label, block := range labelsMap {
+ if blockMap, ok := block.(map[string]any); ok {
+ s.WriteString(formatBlock(blockName, label, blockMap, 0))
+ }
+ }
+ }
+ }
+
+ return s.String()
+}
+
+func formatBlock(blockName string, label string, block map[string]any, indent int) string {
+ var s strings.Builder
+
+ s.WriteString(strings.Repeat(" ", indent))
+ s.WriteString(blockName)
+ if label != "" {
+ s.WriteString(fmt.Sprintf(` %s`, strconv.Quote(label)))
+ }
+ s.WriteString(" {\n")
+
+ var blocks []any
+ if blocksValue, exists := block["blocks"]; exists {
+ if blocksList, ok := blocksValue.([]any); ok {
+ blocks = blocksList
+ }
+ delete(block, "blocks")
+ }
+
+ for _, key := range slices.Sorted(maps.Keys(block)) {
+ s.WriteString(strings.Repeat(" ", indent+1))
+ s.WriteString(fmt.Sprintf("%s = %s\n", key, formatValue(block[key])))
+ }
+
+ for _, blockItem := range blocks {
+ if blockMap, ok := blockItem.(map[string]any); ok {
+ var name string
+ if nameValue, exists := blockMap["name"]; exists {
+ if nameStr, ok := nameValue.(string); ok {
+ name = nameStr
+ }
+ delete(blockMap, "name")
+ }
+
+ s.WriteString(formatBlock(name, "", blockMap, indent+1))
+ }
+ }
+
+ s.WriteString(strings.Repeat(" ", indent))
+ s.WriteString("}\n")
+
+ return s.String()
+}
+
+func formatValue(value any) string {
+ switch v := value.(type) {
+ case string:
+ return strconv.Quote(v)
+ case map[string]any:
+ if ref, exists := v["$ref"]; exists {
+ if refStr, ok := ref.(string); ok {
+ return refStr
+ }
+ }
+ var parts []string
+ for _, name := range slices.Sorted(maps.Keys(v)) {
+ parts = append(parts, fmt.Sprintf("%s=%s,", name, formatValue(v[name])))
+ }
+ return "{" + strings.Join(parts, " ") + "}"
+ case []any:
+ var parts []string
+ for _, item := range v {
+ parts = append(parts, formatValue(item))
+ }
+ return "[" + strings.Join(parts, ", ") + "]"
+ default:
+ return fmt.Sprintf("%v", v)
+ }
+}
diff --git a/nixos/shared/alloy-nix-config/default.nix b/nixos/shared/alloy-nix-config/default.nix
new file mode 100644
index 0000000..d4efe02
--- /dev/null
+++ b/nixos/shared/alloy-nix-config/default.nix
@@ -0,0 +1,8 @@
+{ pkgs ? import <nixpkgs> {} }:
+
+pkgs.buildGoModule {
+ pname = "alloy-nix-config";
+ version = "git";
+ src = ./.;
+ vendorHash = null;
+}
diff --git a/nixos/shared/alloy-nix-config/go.mod b/nixos/shared/alloy-nix-config/go.mod
new file mode 100644
index 0000000..2916089
--- /dev/null
+++ b/nixos/shared/alloy-nix-config/go.mod
@@ -0,0 +1,3 @@
+module push-f.com/alloy-nix-config
+
+go 1.24.5
diff --git a/nixos/shared/grafana-matrix-forwarder/default.nix b/nixos/shared/grafana-matrix-forwarder/default.nix
new file mode 100644
index 0000000..7a04dcb
--- /dev/null
+++ b/nixos/shared/grafana-matrix-forwarder/default.nix
@@ -0,0 +1,10 @@
+{ buildGoModule }:
+let
+ sources = import <top/npins>;
+in
+buildGoModule {
+ pname = "grafana-matrix-forwarder";
+ version = sources.grafana-matrix-forwarder.version;
+ src = sources.grafana-matrix-forwarder;
+ vendorHash = "sha256-ifkeakyRkIF2Y/4otUWhTvUzsPwRb1Wxx6gqN0806c4=";
+}
diff --git a/nixos/shared/grafana-matrix-forwarder/service.nix b/nixos/shared/grafana-matrix-forwarder/service.nix
new file mode 100644
index 0000000..5ad511c
--- /dev/null
+++ b/nixos/shared/grafana-matrix-forwarder/service.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs, ... }:
+
+let
+ grafanaMatrixForwarder = pkgs.callPackage ./default.nix {};
+ cfg = config.services.grafana-matrix-forwarder;
+in
+{
+ options.services.grafana-matrix-forwarder = {
+ enable = lib.mkEnableOption "grafana-matrix-forwarder";
+ port = lib.mkOption {
+ type = lib.types.int;
+ };
+ homeserver = lib.mkOption {
+ type = lib.types.str;
+ };
+ environmentFile = lib.mkOption {
+ type = lib.types.path;
+ };
+ };
+
+ config = lib.mkIf cfg.enable {
+ systemd.services.grafana-matrix-forwarder = {
+ serviceConfig = {
+ ExecStart = "${grafanaMatrixForwarder}/bin/grafana-matrix-forwarder --port=${toString cfg.port} --homeserver ${cfg.homeserver}";
+ EnvironmentFile = cfg.environmentFile;
+ DynamicUser = "true";
+ };
+ wantedBy = ["multi-user.target"];
+ };
+ };
+}
diff --git a/nixos/shared/prometheus-sql-exporter/default.nix b/nixos/shared/prometheus-sql-exporter/default.nix
index 81f1660..5d80a62 100644
--- a/nixos/shared/prometheus-sql-exporter/default.nix
+++ b/nixos/shared/prometheus-sql-exporter/default.nix
@@ -1,21 +1,15 @@
{
lib,
buildGoModule,
- fetchFromGitHub,
}:
-
-buildGoModule rec {
+let
+ sources = import <top/npins>;
+in
+buildGoModule {
pname = "sql_exporter";
- version = "0.17.1";
-
- src = fetchFromGitHub {
- owner = "burningalchemist";
- repo = pname;
- rev = version;
- sha256 = "sha256-AEPFXPplHtny1P3gMvB1gbMj10bpu9PXc6ywliF+dCc=";
- };
-
- vendorHash = "sha256-KFWDqbdbXvgEtz1nlasWrvIckpzasUdzbb+AKfXmYf8=";
+ version = sources.prometheus-sql-exporter.version;
+ src = sources.prometheus-sql-exporter;
+ vendorHash = "sha256-eZxxmqoiXPdjZs/lwbzvWco9mDFy0zmpGDcqTIyWbK4=";
meta = with lib; {
description = "Database-agnostic SQL exporter for Prometheus";
diff --git a/nixos/shared/prometheus-sql-exporter/service.nix b/nixos/shared/prometheus-sql-exporter/service.nix
index a887f91..a79528c 100644
--- a/nixos/shared/prometheus-sql-exporter/service.nix
+++ b/nixos/shared/prometheus-sql-exporter/service.nix
@@ -4,7 +4,16 @@
let
sqlExporter = pkgs.callPackage ./default.nix {};
cfg = config.services.prometheus-sql-exporter;
- configFile = builtins.toFile "config.yaml" (builtins.toJSON cfg.config);
+ configFile = builtins.toFile "config.yaml" (builtins.toJSON cfg.config);
+ validateConfig = file:
+ pkgs.runCommand "validate-config"
+ {
+ nativeBuildInputs = [sqlExporter];
+ }
+ ''
+ sql_exporter -config.check -config.file "${file}"
+ ln -s "${file}" "$out"
+ '';
in
{
options.services.prometheus-sql-exporter = {
@@ -20,7 +29,7 @@ in
config = lib.mkIf cfg.enable {
systemd.services.prometheus-sql-exporter = {
serviceConfig = {
- ExecStart = "${sqlExporter}/bin/sql_exporter -config.file ${configFile} -web.listen-address :${toString cfg.port}";
+ ExecStart = "${sqlExporter}/bin/sql_exporter -config.file ${validateConfig configFile} -web.listen-address :${toString cfg.port}";
DynamicUser = "true";
User = "prometheus-sql-exporter";
};