From 9237328fb09b2c18365bb5fcef1c4596d77973a9 Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Sat, 17 Oct 2026 11:14:02 +0200 Subject: refactor: make server packages buildable via packages.* --- nixos/default.nix | 11 +- nixos/helpers.nix | 2 +- nixos/hosts/ev/exporters.nix | 4 - nixos/hosts/tente/exporters.nix | 5 - nixos/profiles/server/default.nix | 2 + .../pkgs/alloy-nix-config/alloy_nix_config.go | 129 +++++++++++++++++++++ nixos/profiles/server/pkgs/alloy-nix-config/go.mod | 3 + .../server/pkgs/alloy-nix-config/package.nix | 8 ++ .../server/pkgs/prometheus-sql-exporter.nix | 18 +++ .../server/pkgs/prometheus-storagebox-exporter.nix | 22 ++++ nixos/profiles/server/prometheus-sql-exporter.nix | 39 +++++++ .../server/prometheus-storagebox-exporter.nix | 33 ++++++ nixos/shared/alloy-nix-config/alloy_nix_config.go | 129 --------------------- nixos/shared/alloy-nix-config/default.nix | 8 -- nixos/shared/alloy-nix-config/go.mod | 3 - nixos/shared/prometheus-sql-exporter/default.nix | 18 --- nixos/shared/prometheus-sql-exporter/service.nix | 39 ------- .../prometheus-storagebox-exporter/default.nix | 22 ---- .../prometheus-storagebox-exporter/service.nix | 33 ------ 19 files changed, 265 insertions(+), 263 deletions(-) create mode 100644 nixos/profiles/server/pkgs/alloy-nix-config/alloy_nix_config.go create mode 100644 nixos/profiles/server/pkgs/alloy-nix-config/go.mod create mode 100644 nixos/profiles/server/pkgs/alloy-nix-config/package.nix create mode 100644 nixos/profiles/server/pkgs/prometheus-sql-exporter.nix create mode 100644 nixos/profiles/server/pkgs/prometheus-storagebox-exporter.nix create mode 100644 nixos/profiles/server/prometheus-sql-exporter.nix create mode 100644 nixos/profiles/server/prometheus-storagebox-exporter.nix delete mode 100644 nixos/shared/alloy-nix-config/alloy_nix_config.go delete mode 100644 nixos/shared/alloy-nix-config/default.nix delete mode 100644 nixos/shared/alloy-nix-config/go.mod delete mode 100644 nixos/shared/prometheus-sql-exporter/default.nix delete mode 100644 nixos/shared/prometheus-sql-exporter/service.nix delete mode 100644 nixos/shared/prometheus-storagebox-exporter/default.nix delete mode 100644 nixos/shared/prometheus-storagebox-exporter/service.nix (limited to 'nixos') diff --git a/nixos/default.nix b/nixos/default.nix index e5090db..c164c7f 100644 --- a/nixos/default.nix +++ b/nixos/default.nix @@ -53,5 +53,14 @@ rec { standaloneConfigurations = mkConfigurations (host: host.standaloneModule) hostDefs; - standaloneSystems = mkSystems standaloneConfigurations; + packages = { + server = + let + pkgs = import sources.nixos-small.outPath { }; + in + pkgs.lib.packagesFromDirectoryRecursive { + directory = ./profiles/server/pkgs; + callPackage = pkgs.newScope { inherit sources; }; + }; + }; } diff --git a/nixos/helpers.nix b/nixos/helpers.nix index 4ee33ef..49877a5 100644 --- a/nixos/helpers.nix +++ b/nixos/helpers.nix @@ -83,7 +83,7 @@ writeAlloyConfig = cfg: let - alloy-nix-config = "${pkgs.callPackage ./shared/alloy-nix-config {}}/bin/alloy-nix-config"; + alloy-nix-config = "${pkgs.callPackage ./profiles/server/pkgs/alloy-nix-config/package.nix {}}/bin/alloy-nix-config"; in pkgs.runCommand "generated-config" {} '' ${alloy-nix-config} ${pkgs.writeText "input.json" (builtins.toJSON cfg)} $out diff --git a/nixos/hosts/ev/exporters.nix b/nixos/hosts/ev/exporters.nix index f22f043..aa4c773 100644 --- a/nixos/hosts/ev/exporters.nix +++ b/nixos/hosts/ev/exporters.nix @@ -11,10 +11,6 @@ in }; }; - imports = [ - ../../shared/prometheus-sql-exporter/service.nix - ]; - config = { services.prometheus-sql-exporter = { enable = true; diff --git a/nixos/hosts/tente/exporters.nix b/nixos/hosts/tente/exporters.nix index f146567..0eb9199 100644 --- a/nixos/hosts/tente/exporters.nix +++ b/nixos/hosts/tente/exporters.nix @@ -14,11 +14,6 @@ in }; }; - imports = [ - ../../shared/prometheus-sql-exporter/service.nix - ../../shared/prometheus-storagebox-exporter/service.nix - ]; - config = { services.prometheus-sql-exporter = { enable = true; diff --git a/nixos/profiles/server/default.nix b/nixos/profiles/server/default.nix index 6e744a6..3e917c2 100644 --- a/nixos/profiles/server/default.nix +++ b/nixos/profiles/server/default.nix @@ -4,6 +4,8 @@ imports = [ ../common ./detect-hostname-change.nix + ./prometheus-sql-exporter.nix + ./prometheus-storagebox-exporter.nix ]; services = { diff --git a/nixos/profiles/server/pkgs/alloy-nix-config/alloy_nix_config.go b/nixos/profiles/server/pkgs/alloy-nix-config/alloy_nix_config.go new file mode 100644 index 0000000..4b6eb63 --- /dev/null +++ b/nixos/profiles/server/pkgs/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 \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/profiles/server/pkgs/alloy-nix-config/go.mod b/nixos/profiles/server/pkgs/alloy-nix-config/go.mod new file mode 100644 index 0000000..2916089 --- /dev/null +++ b/nixos/profiles/server/pkgs/alloy-nix-config/go.mod @@ -0,0 +1,3 @@ +module push-f.com/alloy-nix-config + +go 1.24.5 diff --git a/nixos/profiles/server/pkgs/alloy-nix-config/package.nix b/nixos/profiles/server/pkgs/alloy-nix-config/package.nix new file mode 100644 index 0000000..981b612 --- /dev/null +++ b/nixos/profiles/server/pkgs/alloy-nix-config/package.nix @@ -0,0 +1,8 @@ +{ buildGoModule }: + +buildGoModule { + pname = "alloy-nix-config"; + version = "git"; + src = ./.; + vendorHash = null; +} diff --git a/nixos/profiles/server/pkgs/prometheus-sql-exporter.nix b/nixos/profiles/server/pkgs/prometheus-sql-exporter.nix new file mode 100644 index 0000000..e049396 --- /dev/null +++ b/nixos/profiles/server/pkgs/prometheus-sql-exporter.nix @@ -0,0 +1,18 @@ +{ + lib, + buildGoModule, + sources, +}: +buildGoModule { + pname = "sql_exporter"; + 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"; + mainProgram = "sql_exporter"; + homepage = "https://github.com/burningalchemist/sql_exporter"; + license = licenses.mit; + }; +} diff --git a/nixos/profiles/server/pkgs/prometheus-storagebox-exporter.nix b/nixos/profiles/server/pkgs/prometheus-storagebox-exporter.nix new file mode 100644 index 0000000..d4fd13e --- /dev/null +++ b/nixos/profiles/server/pkgs/prometheus-storagebox-exporter.nix @@ -0,0 +1,22 @@ +{ + lib, + buildGoModule, + sources, +}: +buildGoModule { + pname = "storagebox_exporter"; + version = sources.prometheus-storagebox-exporter.version; + src = sources.prometheus-storagebox-exporter; + vendorHash = "sha256-3aBklYmPHELcLG+3sOEDd9qkrJyDrC52LfhOOY/6sOc="; + + meta = with lib; { + description = "Modern Prometheus exporter for Hetzner Storage Box with comprehensive metrics"; + mainProgram = "prometheus-storagebox-exporter"; + homepage = "https://github.com/crstian19/prometheus-storagebox-exporter"; + license = licenses.mit; + }; + + postPatch = '' + substituteInPlace go.mod --replace 'go 1.25.5' 'go 1.25.4' + ''; +} diff --git a/nixos/profiles/server/prometheus-sql-exporter.nix b/nixos/profiles/server/prometheus-sql-exporter.nix new file mode 100644 index 0000000..fe2ef7b --- /dev/null +++ b/nixos/profiles/server/prometheus-sql-exporter.nix @@ -0,0 +1,39 @@ +# TODO: submit burningalchemist's sql_exporter to nixpkgs +{ config, lib, pkgs, sources, ... }: + +let + sqlExporter = pkgs.callPackage ./pkgs/prometheus-sql-exporter.nix { inherit sources; }; + cfg = config.services.prometheus-sql-exporter; + 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 = { + enable = lib.mkEnableOption "sql-exporter"; + port = lib.mkOption { + type = lib.types.int; + }; + config = lib.mkOption { + type = (pkgs.formats.json {}).type; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.prometheus-sql-exporter = { + serviceConfig = { + ExecStart = "${sqlExporter}/bin/sql_exporter -config.file ${validateConfig configFile} -web.listen-address :${toString cfg.port}"; + DynamicUser = "true"; + User = "prometheus-sql-exporter"; + }; + wantedBy = ["multi-user.target"]; + }; + }; +} diff --git a/nixos/profiles/server/prometheus-storagebox-exporter.nix b/nixos/profiles/server/prometheus-storagebox-exporter.nix new file mode 100644 index 0000000..16b03e6 --- /dev/null +++ b/nixos/profiles/server/prometheus-storagebox-exporter.nix @@ -0,0 +1,33 @@ +# TODO: submit Christian's storagebox exporter to nixpkgs +{ config, lib, pkgs, sources, ... }: + +let + storageboxExporter = pkgs.callPackage ./pkgs/prometheus-storagebox-exporter.nix { inherit sources; }; + cfg = config.services.prometheus-storagebox-exporter; +in +{ + options.services.prometheus-storagebox-exporter = { + enable = lib.mkEnableOption "storagebox-exporter"; + port = lib.mkOption { + type = lib.types.int; + }; + hetznerTokenFile = lib.mkOption { + type = lib.types.path; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.storagebox-exporter = { + serviceConfig = { + ExecStart = "${storageboxExporter}/bin/prometheus-storagebox-exporter"; + DynamicUser = "true"; + LoadCredential = "hetznerToken:${cfg.hetznerTokenFile}"; + }; + environment = { + HETZNER_TOKEN_FILE = "%d/hetznerToken"; + LISTEN_ADDRESS = ":${toString cfg.port}"; + }; + wantedBy = ["multi-user.target"]; + }; + }; +} diff --git a/nixos/shared/alloy-nix-config/alloy_nix_config.go b/nixos/shared/alloy-nix-config/alloy_nix_config.go deleted file mode 100644 index 4b6eb63..0000000 --- a/nixos/shared/alloy-nix-config/alloy_nix_config.go +++ /dev/null @@ -1,129 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "maps" - "os" - "slices" - "strconv" - "strings" -) - -func main() { - if len(os.Args) != 3 { - fmt.Fprintf(os.Stderr, "usage: %s \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 deleted file mode 100644 index d4efe02..0000000 --- a/nixos/shared/alloy-nix-config/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ pkgs ? import {} }: - -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 deleted file mode 100644 index 2916089..0000000 --- a/nixos/shared/alloy-nix-config/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module push-f.com/alloy-nix-config - -go 1.24.5 diff --git a/nixos/shared/prometheus-sql-exporter/default.nix b/nixos/shared/prometheus-sql-exporter/default.nix deleted file mode 100644 index e049396..0000000 --- a/nixos/shared/prometheus-sql-exporter/default.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ - lib, - buildGoModule, - sources, -}: -buildGoModule { - pname = "sql_exporter"; - 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"; - mainProgram = "sql_exporter"; - homepage = "https://github.com/burningalchemist/sql_exporter"; - license = licenses.mit; - }; -} diff --git a/nixos/shared/prometheus-sql-exporter/service.nix b/nixos/shared/prometheus-sql-exporter/service.nix deleted file mode 100644 index 3bbf5c2..0000000 --- a/nixos/shared/prometheus-sql-exporter/service.nix +++ /dev/null @@ -1,39 +0,0 @@ -# TODO: submit burningalchemist's sql_exporter to nixpkgs -{ config, lib, pkgs, sources, ... }: - -let - sqlExporter = pkgs.callPackage ./default.nix { inherit sources; }; - cfg = config.services.prometheus-sql-exporter; - 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 = { - enable = lib.mkEnableOption "sql-exporter"; - port = lib.mkOption { - type = lib.types.int; - }; - config = lib.mkOption { - type = (pkgs.formats.json {}).type; - }; - }; - - config = lib.mkIf cfg.enable { - systemd.services.prometheus-sql-exporter = { - serviceConfig = { - ExecStart = "${sqlExporter}/bin/sql_exporter -config.file ${validateConfig configFile} -web.listen-address :${toString cfg.port}"; - DynamicUser = "true"; - User = "prometheus-sql-exporter"; - }; - wantedBy = ["multi-user.target"]; - }; - }; -} diff --git a/nixos/shared/prometheus-storagebox-exporter/default.nix b/nixos/shared/prometheus-storagebox-exporter/default.nix deleted file mode 100644 index d4fd13e..0000000 --- a/nixos/shared/prometheus-storagebox-exporter/default.nix +++ /dev/null @@ -1,22 +0,0 @@ -{ - lib, - buildGoModule, - sources, -}: -buildGoModule { - pname = "storagebox_exporter"; - version = sources.prometheus-storagebox-exporter.version; - src = sources.prometheus-storagebox-exporter; - vendorHash = "sha256-3aBklYmPHELcLG+3sOEDd9qkrJyDrC52LfhOOY/6sOc="; - - meta = with lib; { - description = "Modern Prometheus exporter for Hetzner Storage Box with comprehensive metrics"; - mainProgram = "prometheus-storagebox-exporter"; - homepage = "https://github.com/crstian19/prometheus-storagebox-exporter"; - license = licenses.mit; - }; - - postPatch = '' - substituteInPlace go.mod --replace 'go 1.25.5' 'go 1.25.4' - ''; -} diff --git a/nixos/shared/prometheus-storagebox-exporter/service.nix b/nixos/shared/prometheus-storagebox-exporter/service.nix deleted file mode 100644 index 1d3cff5..0000000 --- a/nixos/shared/prometheus-storagebox-exporter/service.nix +++ /dev/null @@ -1,33 +0,0 @@ -# TODO: submit Christian's storagebox exporter to nixpkgs -{ config, lib, pkgs, sources, ... }: - -let - storageboxExporter = pkgs.callPackage ./default.nix { inherit sources; }; - cfg = config.services.prometheus-storagebox-exporter; -in -{ - options.services.prometheus-storagebox-exporter = { - enable = lib.mkEnableOption "storagebox-exporter"; - port = lib.mkOption { - type = lib.types.int; - }; - hetznerTokenFile = lib.mkOption { - type = lib.types.path; - }; - }; - - config = lib.mkIf cfg.enable { - systemd.services.storagebox-exporter = { - serviceConfig = { - ExecStart = "${storageboxExporter}/bin/prometheus-storagebox-exporter"; - DynamicUser = "true"; - LoadCredential = "hetznerToken:${cfg.hetznerTokenFile}"; - }; - environment = { - HETZNER_TOKEN_FILE = "%d/hetznerToken"; - LISTEN_ADDRESS = ":${toString cfg.port}"; - }; - wantedBy = ["multi-user.target"]; - }; - }; -} -- cgit v1.3.1