1{ lib, pkgs, config, generators, ... }:
2with lib;
3let
4 cfg = config.services.grafana-agent;
5 settingsFormat = pkgs.formats.yaml { };
6 configFile = settingsFormat.generate "grafana-agent.yaml" cfg.settings;
7in
8{
9 meta = {
10 maintainers = with maintainers; [ flokli zimbatm ];
11 };
12
13 options.services.grafana-agent = {
14 enable = mkEnableOption (lib.mdDoc "grafana-agent");
15
16 package = mkPackageOptionMD pkgs "grafana-agent" { };
17
18 credentials = mkOption {
19 description = lib.mdDoc ''
20 Credentials to load at service startup. Keys that are UPPER_SNAKE will be loaded as env vars. Values are absolute paths to the credentials.
21 '';
22 type = types.attrsOf types.str;
23 default = { };
24
25 example = {
26 logs_remote_write_password = "/run/keys/grafana_agent_logs_remote_write_password";
27 LOGS_REMOTE_WRITE_URL = "/run/keys/grafana_agent_logs_remote_write_url";
28 LOGS_REMOTE_WRITE_USERNAME = "/run/keys/grafana_agent_logs_remote_write_username";
29 metrics_remote_write_password = "/run/keys/grafana_agent_metrics_remote_write_password";
30 METRICS_REMOTE_WRITE_URL = "/run/keys/grafana_agent_metrics_remote_write_url";
31 METRICS_REMOTE_WRITE_USERNAME = "/run/keys/grafana_agent_metrics_remote_write_username";
32 };
33 };
34
35 extraFlags = mkOption {
36 type = with types; listOf str;
37 default = [ ];
38 example = [ "-enable-features=integrations-next" "-disable-reporting" ];
39 description = lib.mdDoc ''
40 Extra command-line flags passed to {command}`grafana-agent`.
41
42 See <https://grafana.com/docs/agent/latest/static/configuration/flags/>
43 '';
44 };
45
46 settings = mkOption {
47 description = lib.mdDoc ''
48 Configuration for {command}`grafana-agent`.
49
50 See <https://grafana.com/docs/agent/latest/configuration/>
51 '';
52
53 type = types.submodule {
54 freeformType = settingsFormat.type;
55 };
56
57 default = { };
58 defaultText = lib.literalExpression ''
59 {
60 metrics = {
61 wal_directory = "\''${STATE_DIRECTORY}";
62 global.scrape_interval = "5s";
63 };
64 integrations = {
65 agent.enabled = true;
66 agent.scrape_integration = true;
67 node_exporter.enabled = true;
68 };
69 }
70 '';
71 example = {
72 metrics.global.remote_write = [{
73 url = "\${METRICS_REMOTE_WRITE_URL}";
74 basic_auth.username = "\${METRICS_REMOTE_WRITE_USERNAME}";
75 basic_auth.password_file = "\${CREDENTIALS_DIRECTORY}/metrics_remote_write_password";
76 }];
77 logs.configs = [{
78 name = "default";
79 scrape_configs = [
80 {
81 job_name = "journal";
82 journal = {
83 max_age = "12h";
84 labels.job = "systemd-journal";
85 };
86 relabel_configs = [
87 {
88 source_labels = [ "__journal__systemd_unit" ];
89 target_label = "systemd_unit";
90 }
91 {
92 source_labels = [ "__journal__hostname" ];
93 target_label = "nodename";
94 }
95 {
96 source_labels = [ "__journal_syslog_identifier" ];
97 target_label = "syslog_identifier";
98 }
99 ];
100 }
101 ];
102 positions.filename = "\${STATE_DIRECTORY}/loki_positions.yaml";
103 clients = [{
104 url = "\${LOGS_REMOTE_WRITE_URL}";
105 basic_auth.username = "\${LOGS_REMOTE_WRITE_USERNAME}";
106 basic_auth.password_file = "\${CREDENTIALS_DIRECTORY}/logs_remote_write_password";
107 }];
108 }];
109 };
110 };
111 };
112
113 config = mkIf cfg.enable {
114 services.grafana-agent.settings = {
115 # keep this in sync with config.services.grafana-agent.settings.defaultText.
116 metrics = {
117 wal_directory = mkDefault "\${STATE_DIRECTORY}";
118 global.scrape_interval = mkDefault "5s";
119 };
120 integrations = {
121 agent.enabled = mkDefault true;
122 agent.scrape_integration = mkDefault true;
123 node_exporter.enabled = mkDefault true;
124 };
125 };
126
127 systemd.services.grafana-agent = {
128 wantedBy = [ "multi-user.target" ];
129 script = ''
130 set -euo pipefail
131 shopt -u nullglob
132
133 # Load all credentials into env if they are in UPPER_SNAKE form.
134 if [[ -n "''${CREDENTIALS_DIRECTORY:-}" ]]; then
135 for file in "$CREDENTIALS_DIRECTORY"/*; do
136 key=$(basename "$file")
137 if [[ $key =~ ^[A-Z0-9_]+$ ]]; then
138 echo "Environ $key"
139 export "$key=$(< "$file")"
140 fi
141 done
142 fi
143
144 # We can't use Environment=HOSTNAME=%H, as it doesn't include the domain part.
145 export HOSTNAME=$(< /proc/sys/kernel/hostname)
146
147 exec ${lib.getExe cfg.package} -config.expand-env -config.file ${configFile} ${escapeShellArgs cfg.extraFlags}
148 '';
149 serviceConfig = {
150 Restart = "always";
151 DynamicUser = true;
152 RestartSec = 2;
153 SupplementaryGroups = [
154 # allow to read the systemd journal for loki log forwarding
155 "systemd-journal"
156 ];
157 StateDirectory = "grafana-agent";
158 LoadCredential = lib.mapAttrsToList (key: value: "${key}:${value}") cfg.credentials;
159 Type = "simple";
160 };
161 };
162 };
163}