homelab/modules/homelab/services/monitoring/grafana_new.nix
plasmagoat bcbcc8b17b
Some checks failed
Test / tests (push) Has been cancelled
/ OpenTofu (push) Has been cancelled
homelab framework module init (everything is a mess)
2025-07-28 02:05:13 +02:00

369 lines
9.2 KiB
Nix

{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.homelab.services.grafana;
homelabCfg = config.homelab;
# Dashboard provisioning
provisionDashboard = name: source: {
"grafana-dashboards/${name}.json" = {
inherit source;
user = "grafana";
group = "grafana";
mode = "0644";
};
};
# Generate all dashboard files
dashboardFiles =
fold (
dashboard: acc:
acc // (provisionDashboard dashboard.name dashboard.source)
) {}
cfg.dashboards.files;
in {
options.homelab.services.grafana = {
enable = mkEnableOption "Grafana dashboard service";
port = mkOption {
type = types.port;
default = 3000;
description = "Port for Grafana web interface";
};
openFirewall = mkOption {
type = types.bool;
default = true;
description = "Whether to open firewall ports";
};
dataDir = mkOption {
type = types.str;
default = "/var/lib/grafana";
description = "Directory to store Grafana data";
};
domain = mkOption {
type = types.str;
default = "grafana.${homelabCfg.externalDomain}";
description = "Domain for Grafana";
};
rootUrl = mkOption {
type = types.str;
default = "https://grafana.${homelabCfg.externalDomain}";
description = "Root URL for Grafana";
};
# Authentication settings
auth = {
disableLoginForm = mkOption {
type = types.bool;
default = false;
description = "Disable the login form";
};
oauthAutoLogin = mkOption {
type = types.bool;
default = false;
description = "Enable OAuth auto-login";
};
genericOauth = {
enabled = mkOption {
type = types.bool;
default = false;
description = "Enable generic OAuth";
};
};
};
# Data source configuration
datasources = {
prometheus = {
enable = mkOption {
type = types.bool;
default = true;
description = "Enable Prometheus datasource";
};
url = mkOption {
type = types.str;
default = "http://127.0.0.1:9090";
description = "Prometheus URL";
};
uid = mkOption {
type = types.str;
default = "prometheus";
description = "Unique identifier for Prometheus datasource";
};
};
loki = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable Loki datasource";
};
url = mkOption {
type = types.str;
default = "http://127.0.0.1:3100";
description = "Loki URL";
};
uid = mkOption {
type = types.str;
default = "loki";
description = "Unique identifier for Loki datasource";
};
};
influxdb = {
enable = mkOption {
type = types.bool;
default = false;
description = "Enable InfluxDB datasource";
};
url = mkOption {
type = types.str;
default = "http://127.0.0.1:8086";
description = "InfluxDB URL";
};
database = mkOption {
type = types.str;
default = "homelab";
description = "InfluxDB database name";
};
tokenPath = mkOption {
type = types.nullOr types.path;
default = null;
description = "Path to InfluxDB token file";
};
uid = mkOption {
type = types.str;
default = "influxdb";
description = "Unique identifier for InfluxDB datasource";
};
};
extra = mkOption {
type = types.listOf types.attrs;
default = [];
description = "Additional data sources";
};
};
# Dashboard configuration
dashboards = {
path = mkOption {
type = types.str;
default = "/etc/grafana-dashboards";
description = "Path to dashboard files";
};
files = mkOption {
type = types.listOf (types.submodule {
options = {
name = mkOption {
type = types.str;
description = "Dashboard name (without .json extension)";
example = "node-exporter";
};
source = mkOption {
type = types.path;
description = "Path to dashboard JSON file";
};
};
});
default = [];
description = "Dashboard files to provision";
example = literalExpression ''
[
{
name = "node-exporter";
source = ./dashboards/node-exporter.json;
}
{
name = "traefik";
source = ./dashboards/traefik.json;
}
]
'';
};
};
# Extra user groups for accessing secrets
extraGroups = mkOption {
type = types.listOf types.str;
default = [];
description = "Additional groups for the grafana user";
example = ["influxdb2"];
};
# Additional settings
extraSettings = mkOption {
type = types.attrs;
default = {};
description = "Additional Grafana settings";
};
plugins = mkOption {
type = types.listOf types.package;
default = [];
description = "Grafana plugins to install";
example = literalExpression "with pkgs.grafanaPlugins; [ grafana-piechart-panel ]";
};
};
config = mkIf cfg.enable {
# Add grafana user to extra groups (e.g., for accessing secrets)
users.users.grafana.extraGroups = cfg.extraGroups;
services.grafana = {
enable = true;
dataDir = cfg.dataDir;
declarativePlugins = cfg.plugins;
settings =
recursiveUpdate {
server = {
http_port = cfg.port;
http_addr = "0.0.0.0";
domain = cfg.domain;
root_url = cfg.rootUrl;
oauth_auto_login = cfg.auth.oauthAutoLogin;
};
"auth.generic_oauth" = {
enabled = cfg.auth.genericOauth.enabled;
};
auth = {
disable_login_form = cfg.auth.disableLoginForm;
};
}
cfg.extraSettings;
provision = {
enable = true;
datasources.settings = {
datasources = let
# Build datasource list
datasources =
[]
++ optional cfg.datasources.prometheus.enable {
uid = cfg.datasources.prometheus.uid;
name = "Prometheus";
type = "prometheus";
url = cfg.datasources.prometheus.url;
}
++ optional cfg.datasources.loki.enable {
uid = cfg.datasources.loki.uid;
name = "Loki";
type = "loki";
url = cfg.datasources.loki.url;
}
++ optional cfg.datasources.influxdb.enable {
uid = cfg.datasources.influxdb.uid;
name = "InfluxDB";
type = "influxdb";
url = cfg.datasources.influxdb.url;
access = "proxy";
jsonData = {
dbName = cfg.datasources.influxdb.database;
httpHeaderName1 = "Authorization";
};
secureJsonData = mkIf (cfg.datasources.influxdb.tokenPath != null) {
httpHeaderValue1 = "$__file{${cfg.datasources.influxdb.tokenPath}}";
};
}
++ cfg.datasources.extra;
in
datasources;
};
dashboards.settings.providers = mkIf (cfg.dashboards.files != []) [
{
name = "homelab-dashboards";
options.path = cfg.dashboards.path;
}
];
};
};
# Open firewall if requested
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [cfg.port];
# Provision dashboard files
environment.etc = dashboardFiles;
# Add to monitoring endpoints
homelab.monitoring.metrics = [
{
name = "grafana";
port = cfg.port;
path = "/metrics";
jobName = "grafana";
labels = {
service = "grafana";
component = "monitoring";
};
}
];
# Add health checks
homelab.monitoring.healthChecks = [
{
name = "grafana-web-interface";
port = cfg.port;
path = "/api/health";
interval = "30s";
conditions = [
"[STATUS] == 200"
"[BODY].database == ok"
"[RESPONSE_TIME] < 2000"
];
group = "monitoring";
labels = {
service = "grafana";
component = "web-interface";
};
}
{
name = "grafana-login-page";
port = cfg.port;
path = "/login";
interval = "60s";
conditions = [
"[STATUS] == 200"
"[RESPONSE_TIME] < 3000"
];
group = "monitoring";
labels = {
service = "grafana";
component = "login";
};
}
];
# Add reverse proxy entry
homelab.reverseProxy.entries = [
{
subdomain = "grafana";
host = homelabCfg.hostname;
port = cfg.port;
}
];
};
}