diff options
Diffstat (limited to '')
| -rw-r--r-- | modules/by-name/mo/monitoring/components/grafana.nix | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/modules/by-name/mo/monitoring/components/grafana.nix b/modules/by-name/mo/monitoring/components/grafana.nix new file mode 100644 index 0000000..5459878 --- /dev/null +++ b/modules/by-name/mo/monitoring/components/grafana.nix @@ -0,0 +1,296 @@ +{ + config, + lib, + pkgs, + ... +}: let + cfg = config.vhack.monitoring.grafana; +in { + options.vhack.monitoring.grafana = { + enable = lib.mkEnableOption "grafana"; + + orgId = lib.mkOption { + type = lib.types.int; + description = "Org ID where all vhack related config will be stored."; + default = 1; + }; + + contactPoints = lib.mkOption { + type = lib.types.listOf lib.types.str; + description = "List of email addresses to send alerts to"; + default = []; + }; + + dashboards = lib.mkOption { + type = lib.types.listOf lib.types.path; + description = "dashboards to provision under 'Vhack' folder."; + default = []; + }; + + fqdn = lib.mkOption { + type = lib.types.str; + description = "Domain under which Grafana will be served."; + example = "grafana.example.org"; + }; + + adminPassword = lib.mkOption { + type = lib.types.path; + description = "The age-encrypted initial admin password; passed to agenix"; + }; + + secretKey = lib.mkOption { + type = lib.types.path; + description = "The age-encrypted secret key, used for signing; passed to agenix"; + }; + + port = lib.mkOption { + type = lib.types.port; + description = "Port where Grafana listens to HTTP requests."; + default = 3000; + }; + + debugLog = lib.mkOption { + type = lib.types.bool; + description = "Set to true to enable debug logging of the infrastructure serving Grafana."; + default = false; + example = true; + }; + + smtp = lib.mkOption { + description = "SMTP options."; + default = null; + type = lib.types.nullOr ( + lib.types.submodule { + options = { + from_address = lib.mkOption { + type = lib.types.str; + description = "SMTP address from which the emails originate."; + example = "vaultwarden@mydomain.com"; + }; + from_name = lib.mkOption { + type = lib.types.str; + description = "SMTP name from which the emails originate."; + default = "Grafana"; + }; + host = lib.mkOption { + type = lib.types.str; + description = "SMTP host to send the emails to."; + }; + port = lib.mkOption { + type = lib.types.port; + description = "SMTP port to send the emails to."; + default = 25; + }; + username = lib.mkOption { + type = lib.types.str; + description = "Username to connect to the SMTP host."; + }; + passwordFile = lib.mkOption { + type = lib.types.str; + description = "File containing the password to connect to the SMTP host."; + }; + }; + } + ); + }; + }; + + config = lib.mkIf cfg.enable { + assertions = [ + { + assertion = builtins.length cfg.contactPoints > 0; + message = "Must have at least one contact point for alerting"; + } + ]; + + age.secrets = { + grafanaInitialAdminPw = { + file = cfg.adminPassword; + mode = "0700"; + owner = "grafana"; + group = "grafana"; + }; + grafanaSecretKey = { + file = cfg.secretKey; + mode = "0700"; + owner = "grafana"; + group = "grafana"; + }; + }; + + vhack = { + postgresql.enable = true; + monitoring.grafana.dashboards = [ + ../dashboards/Errors.json + ../dashboards/Performance.json + ../dashboards/Scraping_Jobs.json + ]; + }; + + services = { + nginx = { + virtualHosts.${cfg.fqdn} = { + locations."/" = { + proxyPass = "http://${toString config.services.grafana.settings.server.http_addr}:${toString config.services.grafana.settings.server.http_port}"; + proxyWebsockets = true; + extraConfig = '' + proxy_set_header Host $host; + ''; + }; + + forceSSL = true; + enableACME = true; + }; + }; + + postgresql = { + ensureUsers = [ + { + name = "grafana"; + ensureDBOwnership = true; + } + ]; + ensureDatabases = [ + "grafana" + ]; + }; + + grafana = { + enable = true; + + settings = { + database = { + host = "/run/postgresql"; + user = "grafana"; + name = "grafana"; + type = "postgres"; + }; + + security = { + secret_key = "$__file{${config.age.secrets.grafanaSecretKey.path}}"; + + disable_initial_admin_creation = false; + admin_password = "$__file{${config.age.secrets.grafanaInitialAdminPw.path}}"; + }; + + server = { + http_addr = "127.0.0.1"; + http_port = cfg.port; + domain = cfg.fqdn; + root_url = "https://${cfg.fqdn}"; + router_logging = cfg.debugLog; + }; + + smtp = lib.mkIf (!(isNull cfg.smtp)) { + enabled = true; + inherit (cfg.smtp) from_address from_name; + host = "${cfg.smtp.host}:${toString cfg.smtp.port}"; + user = cfg.smtp.username; + password = "$__file{${cfg.smtp.passwordFile}}"; + }; + }; + + provision = { + dashboards.settings = lib.mkIf (cfg.dashboards != []) { + apiVersion = 1; + providers = [ + { + folder = "Vhack"; + options.path = pkgs.symlinkJoin { + name = "dashboards"; + paths = map (p: pkgs.runCommand "davhackoard" {} "mkdir $out; cp ${p} $out") cfg.dashboards; + }; + allowUiUpdates = true; + disableDeletion = true; + } + ]; + }; + datasources.settings = { + apiVersion = 1; + datasources = [ + { + inherit (cfg) orgId; + name = "Prometheus"; + type = "prometheus"; + url = "http://127.0.0.1:${toString config.services.prometheus.port}"; + uid = "df80f9f5-97d7-4112-91d8-72f523a02b09"; + isDefault = true; + version = 1; + } + { + inherit (cfg) orgId; + name = "Loki"; + type = "loki"; + url = "http://127.0.0.1:${toString config.services.loki.configuration.server.http_listen_port}"; + uid = "cd6cc53e-840c-484d-85f7-96fede324006"; + version = 1; + } + ]; + deleteDatasources = [ + { + inherit (cfg) orgId; + name = "Prometheus"; + } + { + inherit (cfg) orgId; + name = "Loki"; + } + ]; + }; + alerting = { + contactPoints.settings = { + apiVersion = 1; + contactPoints = [ + { + inherit (cfg) orgId; + name = "grafana-default-email"; + receivers = lib.optionals ((builtins.length cfg.contactPoints) > 0) [ + { + uid = "sysadmin"; + type = "email"; + settings.addresses = lib.concatStringsSep ";" cfg.contactPoints; + } + ]; + } + ]; + }; + policies.settings = { + apiVersion = 1; + policies = [ + { + inherit (cfg) orgId; + receiver = "grafana-default-email"; + group_by = [ + "grafana_folder" + "alertname" + ]; + group_wait = "30s"; + group_interval = "5m"; + repeat_interval = "4h"; + } + ]; + # resetPolicies seems to happen after setting the above policies, effectively rolling back + # any updates. + }; + rules.settings = let + rules = builtins.fromJSON (builtins.readFile ../rules.json); + in { + apiVersion = 1; + groups = [ + { + inherit (cfg) orgId; + name = "SysAdmin"; + folder = "Vhack"; + interval = "10m"; + inherit rules; + } + ]; + # deleteRules seems to happen after creating the above rules, effectively rolling back + # any updates. + }; + }; + }; + }; + }; + }; +} |
