1{ options, config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.grafana;
7
8 envOptions = {
9 PATHS_DATA = cfg.dataDir;
10 PATHS_PLUGINS = "${cfg.dataDir}/plugins";
11 PATHS_LOGS = "${cfg.dataDir}/log";
12
13 SERVER_PROTOCOL = cfg.protocol;
14 SERVER_HTTP_ADDR = cfg.addr;
15 SERVER_HTTP_PORT = cfg.port;
16 SERVER_DOMAIN = cfg.domain;
17 SERVER_ROOT_URL = cfg.rootUrl;
18 SERVER_STATIC_ROOT_PATH = cfg.staticRootPath;
19 SERVER_CERT_FILE = cfg.certFile;
20 SERVER_CERT_KEY = cfg.certKey;
21
22 DATABASE_TYPE = cfg.database.type;
23 DATABASE_HOST = cfg.database.host;
24 DATABASE_NAME = cfg.database.name;
25 DATABASE_USER = cfg.database.user;
26 DATABASE_PASSWORD = cfg.database.password;
27 DATABASE_PATH = cfg.database.path;
28 DATABASE_CONN_MAX_LIFETIME = cfg.database.connMaxLifetime;
29
30 SECURITY_ADMIN_USER = cfg.security.adminUser;
31 SECURITY_ADMIN_PASSWORD = cfg.security.adminPassword;
32 SECURITY_SECRET_KEY = cfg.security.secretKey;
33
34 USERS_ALLOW_SIGN_UP = boolToString cfg.users.allowSignUp;
35 USERS_ALLOW_ORG_CREATE = boolToString cfg.users.allowOrgCreate;
36 USERS_AUTO_ASSIGN_ORG = boolToString cfg.users.autoAssignOrg;
37 USERS_AUTO_ASSIGN_ORG_ROLE = cfg.users.autoAssignOrgRole;
38
39 AUTH_ANONYMOUS_ENABLED = boolToString cfg.auth.anonymous.enable;
40 AUTH_ANONYMOUS_ORG_NAME = cfg.auth.anonymous.org_name;
41 AUTH_ANONYMOUS_ORG_ROLE = cfg.auth.anonymous.org_role;
42
43 ANALYTICS_REPORTING_ENABLED = boolToString cfg.analytics.reporting.enable;
44 } // cfg.extraOptions;
45
46in {
47 options.services.grafana = {
48 enable = mkEnableOption "grafana";
49
50 protocol = mkOption {
51 description = "Which protocol to listen.";
52 default = "http";
53 type = types.enum ["http" "https" "socket"];
54 };
55
56 addr = mkOption {
57 description = "Listening address.";
58 default = "127.0.0.1";
59 type = types.str;
60 };
61
62 port = mkOption {
63 description = "Listening port.";
64 default = 3000;
65 type = types.int;
66 };
67
68 domain = mkOption {
69 description = "The public facing domain name used to access grafana from a browser.";
70 default = "localhost";
71 type = types.str;
72 };
73
74 rootUrl = mkOption {
75 description = "Full public facing url.";
76 default = "%(protocol)s://%(domain)s:%(http_port)s/";
77 type = types.str;
78 };
79
80 certFile = mkOption {
81 description = "Cert file for ssl.";
82 default = "";
83 type = types.str;
84 };
85
86 certKey = mkOption {
87 description = "Cert key for ssl.";
88 default = "";
89 type = types.str;
90 };
91
92 staticRootPath = mkOption {
93 description = "Root path for static assets.";
94 default = "${cfg.package}/share/grafana/public";
95 type = types.str;
96 };
97
98 package = mkOption {
99 description = "Package to use.";
100 default = pkgs.grafana;
101 defaultText = "pkgs.grafana";
102 type = types.package;
103 };
104
105 dataDir = mkOption {
106 description = "Data directory.";
107 default = "/var/lib/grafana";
108 type = types.path;
109 };
110
111 database = {
112 type = mkOption {
113 description = "Database type.";
114 default = "sqlite3";
115 type = types.enum ["mysql" "sqlite3" "postgres"];
116 };
117
118 host = mkOption {
119 description = "Database host.";
120 default = "127.0.0.1:3306";
121 type = types.str;
122 };
123
124 name = mkOption {
125 description = "Database name.";
126 default = "grafana";
127 type = types.str;
128 };
129
130 user = mkOption {
131 description = "Database user.";
132 default = "root";
133 type = types.str;
134 };
135
136 password = mkOption {
137 description = "Database password.";
138 default = "";
139 type = types.str;
140 };
141
142 path = mkOption {
143 description = "Database path.";
144 default = "${cfg.dataDir}/data/grafana.db";
145 type = types.path;
146 };
147
148 connMaxLifetime = mkOption {
149 description = ''
150 Sets the maximum amount of time (in seconds) a connection may be reused.
151 For MySQL this setting should be shorter than the `wait_timeout' variable.
152 '';
153 default = 14400;
154 type = types.int;
155 };
156 };
157
158 security = {
159 adminUser = mkOption {
160 description = "Default admin username.";
161 default = "admin";
162 type = types.str;
163 };
164
165 adminPassword = mkOption {
166 description = "Default admin password.";
167 default = "admin";
168 type = types.str;
169 };
170
171 secretKey = mkOption {
172 description = "Secret key used for signing.";
173 default = "SW2YcwTIb9zpOOhoPsMm";
174 type = types.str;
175 };
176 };
177
178 users = {
179 allowSignUp = mkOption {
180 description = "Disable user signup / registration";
181 default = false;
182 type = types.bool;
183 };
184
185 allowOrgCreate = mkOption {
186 description = "Whether user is allowed to create organizations.";
187 default = false;
188 type = types.bool;
189 };
190
191 autoAssignOrg = mkOption {
192 description = "Whether to automatically assign new users to default org.";
193 default = true;
194 type = types.bool;
195 };
196
197 autoAssignOrgRole = mkOption {
198 description = "Default role new users will be auto assigned.";
199 default = "Viewer";
200 type = types.enum ["Viewer" "Editor"];
201 };
202 };
203
204 auth.anonymous = {
205 enable = mkOption {
206 description = "Whether to allow anonymous access";
207 default = false;
208 type = types.bool;
209 };
210 org_name = mkOption {
211 description = "Which organization to allow anonymous access to";
212 default = "Main Org.";
213 type = types.str;
214 };
215 org_role = mkOption {
216 description = "Which role anonymous users have in the organization";
217 default = "Viewer";
218 type = types.str;
219 };
220
221 };
222
223 analytics.reporting = {
224 enable = mkOption {
225 description = "Whether to allow anonymous usage reporting to stats.grafana.net";
226 default = true;
227 type = types.bool;
228 };
229 };
230
231 extraOptions = mkOption {
232 description = ''
233 Extra configuration options passed as env variables as specified in
234 <link xlink:href="http://docs.grafana.org/installation/configuration/">documentation</link>,
235 but without GF_ prefix
236 '';
237 default = {};
238 type = types.attrsOf types.str;
239 };
240 };
241
242 config = mkIf cfg.enable {
243 warnings = optional (
244 cfg.database.password != options.services.grafana.database.password.default ||
245 cfg.security.adminPassword != options.services.grafana.security.adminPassword.default
246 ) "Grafana passwords will be stored as plaintext in the Nix store!";
247
248 environment.systemPackages = [ cfg.package ];
249
250 systemd.services.grafana = {
251 description = "Grafana Service Daemon";
252 wantedBy = ["multi-user.target"];
253 after = ["networking.target"];
254 environment = {
255 QT_QPA_PLATFORM = "offscreen";
256 } // mapAttrs' (n: v: nameValuePair "GF_${n}" (toString v)) envOptions;
257 serviceConfig = {
258 ExecStart = "${cfg.package.bin}/bin/grafana-server -homepath ${cfg.dataDir}";
259 WorkingDirectory = cfg.dataDir;
260 User = "grafana";
261 };
262 preStart = ''
263 ln -fs ${cfg.package}/share/grafana/conf ${cfg.dataDir}
264 ln -fs ${cfg.package}/share/grafana/tools ${cfg.dataDir}
265 '';
266 };
267
268 users.users.grafana = {
269 uid = config.ids.uids.grafana;
270 description = "Grafana user";
271 home = cfg.dataDir;
272 createHome = true;
273 };
274 };
275}