mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-10-03 13:30:36 +00:00
committed by
github-actions[bot]
parent
c081dc2f32
commit
74d0d2cfdf
@@ -5,25 +5,25 @@ with lib;
|
||||
let
|
||||
cfg = config.services.grafana;
|
||||
opt = options.services.grafana;
|
||||
provisioningSettingsFormat = pkgs.formats.yaml {};
|
||||
provisioningSettingsFormat = pkgs.formats.yaml { };
|
||||
declarativePlugins = pkgs.linkFarm "grafana-plugins" (builtins.map (pkg: { name = pkg.pname; path = pkg; }) cfg.declarativePlugins);
|
||||
useMysql = cfg.settings.database.type == "mysql";
|
||||
usePostgresql = cfg.settings.database.type == "postgres";
|
||||
|
||||
settingsFormatIni = pkgs.formats.ini {};
|
||||
settingsFormatIni = pkgs.formats.ini { };
|
||||
configFile = settingsFormatIni.generate "config.ini" cfg.settings;
|
||||
|
||||
mkProvisionCfg = name: attr: provisionCfg:
|
||||
if provisionCfg.path != null
|
||||
then provisionCfg.path
|
||||
then provisionCfg.path
|
||||
else
|
||||
provisioningSettingsFormat.generate "${name}.yaml"
|
||||
(if provisionCfg.settings != null
|
||||
then provisionCfg.settings
|
||||
else {
|
||||
apiVersion = 1;
|
||||
${attr} = [];
|
||||
});
|
||||
then provisionCfg.settings
|
||||
else {
|
||||
apiVersion = 1;
|
||||
${attr} = [ ];
|
||||
});
|
||||
|
||||
datasourceFileOrDir = mkProvisionCfg "datasource" "datasources" cfg.provision.datasources;
|
||||
dashboardFileOrDir = mkProvisionCfg "dashboard" "providers" cfg.provision.dashboards;
|
||||
@@ -35,9 +35,10 @@ let
|
||||
|
||||
notifierFileOrDir = pkgs.writeText "notifier.yaml" (builtins.toJSON notifierConfiguration);
|
||||
|
||||
generateAlertingProvisioningYaml = x: if (cfg.provision.alerting."${x}".path == null)
|
||||
then provisioningSettingsFormat.generate "${x}.yaml" cfg.provision.alerting."${x}".settings
|
||||
else cfg.provision.alerting."${x}".path;
|
||||
generateAlertingProvisioningYaml = x:
|
||||
if (cfg.provision.alerting."${x}".path == null)
|
||||
then provisioningSettingsFormat.generate "${x}.yaml" cfg.provision.alerting."${x}".settings
|
||||
else cfg.provision.alerting."${x}".path;
|
||||
rulesFileOrDir = generateAlertingProvisioningYaml "rules";
|
||||
contactPointsFileOrDir = generateAlertingProvisioningYaml "contactPoints";
|
||||
policiesFileOrDir = generateAlertingProvisioningYaml "policies";
|
||||
@@ -102,7 +103,7 @@ let
|
||||
description = lib.mdDoc "Datasource type. Required.";
|
||||
};
|
||||
access = mkOption {
|
||||
type = types.enum ["proxy" "direct"];
|
||||
type = types.enum [ "proxy" "direct" ];
|
||||
default = "proxy";
|
||||
description = lib.mdDoc "Access mode. proxy or direct (Server or Browser in the UI). Required.";
|
||||
};
|
||||
@@ -170,7 +171,7 @@ let
|
||||
description = lib.mdDoc "Notifier name.";
|
||||
};
|
||||
type = mkOption {
|
||||
type = types.enum ["dingding" "discord" "email" "googlechat" "hipchat" "kafka" "line" "teams" "opsgenie" "pagerduty" "prometheus-alertmanager" "pushover" "sensu" "sensugo" "slack" "telegram" "threema" "victorops" "webhook"];
|
||||
type = types.enum [ "dingding" "discord" "email" "googlechat" "hipchat" "kafka" "line" "teams" "opsgenie" "pagerduty" "prometheus-alertmanager" "pushover" "sensu" "sensugo" "slack" "telegram" "threema" "victorops" "webhook" ];
|
||||
description = lib.mdDoc "Notifier type.";
|
||||
};
|
||||
uid = mkOption {
|
||||
@@ -225,7 +226,8 @@ let
|
||||
};
|
||||
};
|
||||
};
|
||||
in {
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(mkRenamedOptionModule [ "services" "grafana" "protocol" ] [ "services" "grafana" "settings" "server" "protocol" ])
|
||||
(mkRenamedOptionModule [ "services" "grafana" "addr" ] [ "services" "grafana" "settings" "server" "http_addr" ])
|
||||
@@ -354,7 +356,7 @@ in {
|
||||
protocol = mkOption {
|
||||
description = lib.mdDoc "Which protocol to listen.";
|
||||
default = "http";
|
||||
type = types.enum ["http" "https" "h2" "socket"];
|
||||
type = types.enum [ "http" "https" "h2" "socket" ];
|
||||
};
|
||||
|
||||
http_addr = mkOption {
|
||||
@@ -426,7 +428,7 @@ in {
|
||||
type = mkOption {
|
||||
description = lib.mdDoc "Database type.";
|
||||
default = "sqlite3";
|
||||
type = types.enum ["mysql" "sqlite3" "postgres"];
|
||||
type = types.enum [ "mysql" "sqlite3" "postgres" ];
|
||||
};
|
||||
|
||||
host = mkOption {
|
||||
@@ -555,7 +557,7 @@ in {
|
||||
auto_assign_org_role = mkOption {
|
||||
description = lib.mdDoc "Default role new users will be auto assigned.";
|
||||
default = "Viewer";
|
||||
type = types.enum ["Viewer" "Editor" "Admin"];
|
||||
type = types.enum [ "Viewer" "Editor" "Admin" ];
|
||||
};
|
||||
};
|
||||
|
||||
@@ -575,7 +577,7 @@ in {
|
||||
description = lib.mdDoc ''
|
||||
Declaratively provision Grafana's datasources.
|
||||
'';
|
||||
default = {};
|
||||
default = { };
|
||||
type = submodule' {
|
||||
options.settings = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
@@ -595,13 +597,13 @@ in {
|
||||
|
||||
datasources = mkOption {
|
||||
description = lib.mdDoc "List of datasources to insert/update.";
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf grafanaTypes.datasourceConfig;
|
||||
};
|
||||
|
||||
deleteDatasources = mkOption {
|
||||
description = lib.mdDoc "List of datasources that should be deleted from the database.";
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf (types.submodule {
|
||||
options.name = mkOption {
|
||||
description = lib.mdDoc "Name of the datasource to delete.";
|
||||
@@ -650,7 +652,7 @@ in {
|
||||
description = lib.mdDoc ''
|
||||
Declaratively provision Grafana's dashboards.
|
||||
'';
|
||||
default = {};
|
||||
default = { };
|
||||
type = submodule' {
|
||||
options.settings = mkOption {
|
||||
description = lib.mdDoc ''
|
||||
@@ -669,7 +671,7 @@ in {
|
||||
|
||||
options.providers = mkOption {
|
||||
description = lib.mdDoc "List of dashboards to insert/update.";
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf grafanaTypes.dashboardConfig;
|
||||
};
|
||||
});
|
||||
@@ -700,7 +702,7 @@ in {
|
||||
|
||||
notifiers = mkOption {
|
||||
description = lib.mdDoc "Grafana notifier configuration.";
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf grafanaTypes.notifierConfig;
|
||||
apply = x: map _filter x;
|
||||
};
|
||||
@@ -736,7 +738,7 @@ in {
|
||||
|
||||
groups = mkOption {
|
||||
description = lib.mdDoc "List of rule groups to import or update.";
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf (types.submodule {
|
||||
freeformType = provisioningSettingsFormat.type;
|
||||
|
||||
@@ -759,7 +761,7 @@ in {
|
||||
|
||||
deleteRules = mkOption {
|
||||
description = lib.mdDoc "List of alert rule UIDs that should be deleted.";
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf (types.submodule {
|
||||
options.orgId = mkOption {
|
||||
description = lib.mdDoc "Organization ID, default = 1";
|
||||
@@ -860,7 +862,7 @@ in {
|
||||
|
||||
contactPoints = mkOption {
|
||||
description = lib.mdDoc "List of contact points to import or update.";
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf (types.submodule {
|
||||
freeformType = provisioningSettingsFormat.type;
|
||||
|
||||
@@ -873,7 +875,7 @@ in {
|
||||
|
||||
deleteContactPoints = mkOption {
|
||||
description = lib.mdDoc "List of receivers that should be deleted.";
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf (types.submodule {
|
||||
options.orgId = mkOption {
|
||||
description = lib.mdDoc "Organization ID, default = 1.";
|
||||
@@ -941,7 +943,7 @@ in {
|
||||
|
||||
policies = mkOption {
|
||||
description = lib.mdDoc "List of contact points to import or update.";
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf (types.submodule {
|
||||
freeformType = provisioningSettingsFormat.type;
|
||||
});
|
||||
@@ -949,7 +951,7 @@ in {
|
||||
|
||||
resetPolicies = mkOption {
|
||||
description = lib.mdDoc "List of orgIds that should be reset to the default policy.";
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf types.int;
|
||||
};
|
||||
};
|
||||
@@ -1011,7 +1013,7 @@ in {
|
||||
|
||||
templates = mkOption {
|
||||
description = lib.mdDoc "List of templates to import or update.";
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf (types.submodule {
|
||||
freeformType = provisioningSettingsFormat.type;
|
||||
|
||||
@@ -1029,7 +1031,7 @@ in {
|
||||
|
||||
deleteTemplates = mkOption {
|
||||
description = lib.mdDoc "List of alert rule UIDs that should be deleted.";
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf (types.submodule {
|
||||
options.orgId = mkOption {
|
||||
description = lib.mdDoc "Organization ID, default = 1.";
|
||||
@@ -1093,7 +1095,7 @@ in {
|
||||
|
||||
muteTimes = mkOption {
|
||||
description = lib.mdDoc "List of mute time intervals to import or update.";
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf (types.submodule {
|
||||
freeformType = provisioningSettingsFormat.type;
|
||||
|
||||
@@ -1106,7 +1108,7 @@ in {
|
||||
|
||||
deleteMuteTimes = mkOption {
|
||||
description = lib.mdDoc "List of mute time intervals that should be deleted.";
|
||||
default = [];
|
||||
default = [ ];
|
||||
type = types.listOf (types.submodule {
|
||||
options.orgId = mkOption {
|
||||
description = lib.mdDoc "Organization ID, default = 1.";
|
||||
@@ -1168,45 +1170,58 @@ in {
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
warnings = let
|
||||
doesntUseFileProvider = opt: defaultValue:
|
||||
let
|
||||
regex = "${optionalString (defaultValue != null) "^${defaultValue}$|"}^\\$__(file|env)\\{.*}$|^\\$[^_\\$][^ ]+$";
|
||||
in builtins.match regex opt == null;
|
||||
in
|
||||
# Ensure that no custom credentials are leaked into the Nix store. Unless the default value
|
||||
# is specified, this can be achieved by using the file/env provider:
|
||||
# https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#variable-expansion
|
||||
(optional (
|
||||
doesntUseFileProvider cfg.settings.database.password "" ||
|
||||
doesntUseFileProvider cfg.settings.security.admin_password "admin"
|
||||
) ''
|
||||
Grafana passwords will be stored as plaintext in the Nix store!
|
||||
Use file provider or an env-var instead.
|
||||
'')
|
||||
# Warn about deprecated notifiers.
|
||||
++ (optional (cfg.provision.notifiers != []) ''
|
||||
Notifiers are deprecated upstream and will be removed in Grafana 10.
|
||||
Use `services.grafana.provision.alerting.contactPoints` instead.
|
||||
'')
|
||||
# Ensure that `secureJsonData` of datasources provisioned via `datasources.settings`
|
||||
# only uses file/env providers.
|
||||
++ (optional (
|
||||
let
|
||||
datasourcesToCheck = optionals
|
||||
(cfg.provision.datasources.settings != null)
|
||||
cfg.provision.datasources.settings.datasources;
|
||||
declarationUnsafe = { secureJsonData, ... }:
|
||||
secureJsonData != null
|
||||
&& any (flip doesntUseFileProvider null) (attrValues secureJsonData);
|
||||
in any declarationUnsafe datasourcesToCheck
|
||||
) ''
|
||||
Declarations in the `secureJsonData`-block of a datasource will be leaked to the
|
||||
Nix store unless a file-provider or an env-var is used!
|
||||
'')
|
||||
++ (optional (
|
||||
any (x: x.secure_settings != null) cfg.provision.notifiers
|
||||
) "Notifier secure settings will be stored as plaintext in the Nix store! Use file provider instead.");
|
||||
warnings =
|
||||
let
|
||||
doesntUseFileProvider = opt: defaultValue:
|
||||
let regex = "${optionalString (defaultValue != null) "^${defaultValue}$|"}^\\$__(file|env)\\{.*}$|^\\$[^_\\$][^ ]+$";
|
||||
in builtins.match regex opt == null;
|
||||
|
||||
# Ensure that no custom credentials are leaked into the Nix store. Unless the default value
|
||||
# is specified, this can be achieved by using the file/env provider:
|
||||
# https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#variable-expansion
|
||||
passwordWithoutFileProvider = optional
|
||||
(
|
||||
doesntUseFileProvider cfg.settings.database.password "" ||
|
||||
doesntUseFileProvider cfg.settings.security.admin_password "admin"
|
||||
)
|
||||
''
|
||||
Grafana passwords will be stored as plaintext in the Nix store!
|
||||
Use file provider or an env-var instead.
|
||||
'';
|
||||
|
||||
# Warn about deprecated notifiers.
|
||||
deprecatedNotifiers = optional (cfg.provision.notifiers != [ ]) ''
|
||||
Notifiers are deprecated upstream and will be removed in Grafana 10.
|
||||
Use `services.grafana.provision.alerting.contactPoints` instead.
|
||||
'';
|
||||
|
||||
# Ensure that `secureJsonData` of datasources provisioned via `datasources.settings`
|
||||
# only uses file/env providers.
|
||||
secureJsonDataWithoutFileProvider = optional
|
||||
(
|
||||
let
|
||||
datasourcesToCheck = optionals
|
||||
(cfg.provision.datasources.settings != null)
|
||||
cfg.provision.datasources.settings.datasources;
|
||||
declarationUnsafe = { secureJsonData, ... }:
|
||||
secureJsonData != null
|
||||
&& any (flip doesntUseFileProvider null) (attrValues secureJsonData);
|
||||
in
|
||||
any declarationUnsafe datasourcesToCheck
|
||||
)
|
||||
''
|
||||
Declarations in the `secureJsonData`-block of a datasource will be leaked to the
|
||||
Nix store unless a file-provider or an env-var is used!
|
||||
'';
|
||||
|
||||
notifierSecureSettingsWithoutFileProvider = optional
|
||||
(any (x: x.secure_settings != null) cfg.provision.notifiers)
|
||||
"Notifier secure settings will be stored as plaintext in the Nix store! Use file provider instead.";
|
||||
in
|
||||
passwordWithoutFileProvider
|
||||
++ deprecatedNotifiers
|
||||
++ secureJsonDataWithoutFileProvider
|
||||
++ notifierSecureSettingsWithoutFileProvider;
|
||||
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
|
||||
@@ -1216,11 +1231,12 @@ in {
|
||||
message = "Cannot set both datasources settings and datasources path";
|
||||
}
|
||||
{
|
||||
assertion = let
|
||||
prometheusIsNotDirect = opt: all
|
||||
({ type, access, ... }: type == "prometheus" -> access != "direct")
|
||||
opt;
|
||||
in
|
||||
assertion =
|
||||
let
|
||||
prometheusIsNotDirect = opt: all
|
||||
({ type, access, ... }: type == "prometheus" -> access != "direct")
|
||||
opt;
|
||||
in
|
||||
cfg.provision.datasources.settings == null || prometheusIsNotDirect cfg.provision.datasources.settings.datasources;
|
||||
message = "For datasources of type `prometheus`, the `direct` access mode is not supported anymore (since Grafana 9.2.0)";
|
||||
}
|
||||
@@ -1252,8 +1268,8 @@ in {
|
||||
|
||||
systemd.services.grafana = {
|
||||
description = "Grafana Service Daemon";
|
||||
wantedBy = ["multi-user.target"];
|
||||
after = ["networking.target"] ++ lib.optional usePostgresql "postgresql.service" ++ lib.optional useMysql "mysql.service";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "networking.target" ] ++ lib.optional usePostgresql "postgresql.service" ++ lib.optional useMysql "mysql.service";
|
||||
script = ''
|
||||
set -o errexit -o pipefail -o nounset -o errtrace
|
||||
shopt -s inherit_errexit
|
||||
@@ -1309,6 +1325,6 @@ in {
|
||||
createHome = true;
|
||||
group = "grafana";
|
||||
};
|
||||
users.groups.grafana = {};
|
||||
users.groups.grafana = { };
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user