Unverified Commit b0f7b0c4 authored by oddlama's avatar oddlama Committed by GitHub
Browse files

nixos/perses: init (#411771)

parents 4beeeeb4 944ef0ea
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -66,6 +66,8 @@

- [Shoko](https://shokoanime.com), an anime management system. Available as [services.shoko](#opt-services.shoko.enable).

- [perses](https://perses.dev/), the open dashboard tool for Prometheus and other data sources. Available as [services.perses](#opt-services.perses.enable).

- [Drasl](https://github.com/unmojang/drasl), an alternative authentication server for Minecraft. Available as [services.drasl](#opt-services.drasl.enable).

## Backward Incompatibilities {#sec-release-26.05-incompatibilities}
+1 −0
Original line number Diff line number Diff line
@@ -1037,6 +1037,7 @@
  ./services/monitoring/opentelemetry-collector.nix
  ./services/monitoring/osquery.nix
  ./services/monitoring/parsedmarc.nix
  ./services/monitoring/perses.nix
  ./services/monitoring/pgscv.nix
  ./services/monitoring/prometheus/alertmanager-gotify-bridge.nix
  ./services/monitoring/prometheus/alertmanager-irc-relay.nix
+131 −0
Original line number Diff line number Diff line
{
  pkgs,
  lib,
  config,
  utils,
  ...
}:

let
  inherit (lib)
    getExe
    mkOption
    mkEnableOption
    mkPackageOption
    mkIf
    types
    ;

  cfg = config.services.perses;

  settingsFormat = pkgs.formats.yaml { };

  configPath = "/run/perses/config.yaml";
  secretsReplacement = utils.genJqSecretsReplacement {
    loadCredential = true;
  } cfg.settings configPath;

in
{
  options.services.perses = {
    enable = mkEnableOption "perses";

    package = mkPackageOption pkgs "perses" { };

    port = mkOption {
      type = types.port;
      default = 8080;
      description = ''
        Perses Web interface port.
      '';
    };

    listenAddress = mkOption {
      type = types.str;
      default = "";
      description = ''
        Address to listen on. Empty string will listen on all interfaces.
      '';
    };

    settings = mkOption {
      type = types.submodule {
        freeformType = settingsFormat.type;
      };
      description = ''
        Perses settings. See <https://perses.dev/perses/docs/configuration/configuration/> for available options.
        You can specify secret values in this configuration by setting `somevalue._secret = "/path/to/file"` instead of setting `somevalue` directly.
      '';
      default = { };
    };

    extraOptions = mkOption {
      type = types.listOf types.str;
      default = [ ];
      example = [
        "-web.telemetry-path=/metrics"
      ];
      description = "Additional options passed to perses daemon.";
    };
  };

  config = mkIf cfg.enable {
    systemd.services.perses = {
      description = "Perses Daemon";
      wantedBy = [ "multi-user.target" ];
      after = [ "networking.target" ];

      preStart = secretsReplacement.script;

      serviceConfig = rec {
        ExecStart = utils.escapeSystemdExecArgs (
          [
            (getExe cfg.package)
            "-config=${configPath}"
            "-web.listen-address=${cfg.listenAddress}:${toString cfg.port}"
          ]
          ++ cfg.extraOptions
        );

        User = "perses";
        DynamicUser = true;
        Restart = "on-failure";
        RuntimeDirectory = "perses";
        RuntimeDirectoryMode = "0755";
        StateDirectory = "perses";
        WorkingDirectory = "%S/${StateDirectory}";

        LoadCredential = secretsReplacement.credentials;

        # Hardening
        AmbientCapabilities = mkIf (cfg.port < 1024) [ "CAP_NET_BIND_SERVICE" ];
        CapabilityBoundingSet = if (cfg.port < 1024) then [ "CAP_NET_BIND_SERVICE" ] else [ "" ];
        LockPersonality = true;
        NoNewPrivileges = true;
        PrivateDevices = true;
        PrivateTmp = true;
        ProtectClock = true;
        ProtectControlGroups = true;
        ProtectHome = true;
        ProtectHostname = true;
        ProtectKernelLogs = true;
        ProtectKernelModules = true;
        ProtectKernelTunables = true;
        ProtectProc = "invisible";
        ProtectSystem = "full";
        RemoveIPC = true;
        RestrictAddressFamilies = [
          "AF_INET"
          "AF_INET6"
        ];
        RestrictNamespaces = true;
        RestrictRealtime = true;
        RestrictSUIDSGID = true;
        SystemCallArchitectures = "native";
        UMask = "0027";
      };
    };

    environment.systemPackages = [ cfg.package ];
  };
}
+1 −0
Original line number Diff line number Diff line
@@ -1249,6 +1249,7 @@ in
  peerflix = runTest ./peerflix.nix;
  peering-manager = runTest ./web-apps/peering-manager.nix;
  peertube = handleTestOn [ "x86_64-linux" ] ./web-apps/peertube.nix { };
  perses = runTest ./perses.nix;
  pgadmin4 = runTest ./pgadmin4.nix;
  pgbackrest = import ./pgbackrest { inherit runTest; };
  pgbouncer = runTest ./pgbouncer.nix;

nixos/tests/perses.nix

0 → 100644
+65 −0
Original line number Diff line number Diff line
{ pkgs, lib, ... }:

{
  name = "perses";

  meta.maintainers = with lib.maintainers; [ fooker ];

  nodes.prometheus = {
    services.prometheus.enable = true;
    networking.firewall.allowedTCPPorts = [ 9090 ];
  };

  nodes.machine = {
    services.perses = {
      enable = true;

      settings.provisioning.folders = [
        (pkgs.writeTextDir "perses-test-provision-project.yaml" ''
          kind: "Project"
          metadata:
            name: "my-project"
        '')
        (pkgs.writeTextDir "perses-test-provision-datasource.yaml" ''
          kind: "Datasource"
          metadata:
            name: "my-prometheus"
            project: "my-project"
          spec:
            default: true
            plugin:
              kind: "PrometheusDatasource"
              spec:
                proxy:
                  kind: "HTTPProxy"
                  spec:
                    url: "http://prometheus:9090/"
        '')
      ];
    };
  };

  testScript = ''
    start_all()

    prometheus.wait_for_unit("prometheus.service")
    prometheus.wait_for_open_port(9090)

    with subtest("Perses starts"):
        machine.wait_for_unit("perses.service")
        machine.wait_for_open_port(8080)
        machine.succeed("percli login http://127.0.0.1:8080")
        machine.succeed("percli version")
        machine.succeed("percli config")
        machine.succeed("percli plugin list | grep 'Prometheus'")

    with subtest("Query resources"):
        machine.succeed("percli get projects | grep my-project")
        machine.succeed("percli project my-project")
        machine.succeed("percli get datasources | grep my-prometheus")
        machine.succeed("curl --fail -s 127.0.0.1:8080/api/v1/projects/my-project/datasources/my-prometheus")

    with subtest("Datasource check"):
        machine.succeed("curl --fail -s http://127.0.0.1:8080/proxy/projects/my-project/datasources/my-prometheus/api/v1/status/config")
  '';
}
Loading