1{ system ? builtins.currentSystem
2, config ? { }
3, pkgs ? import ../.. { inherit system config; }
4}:
5
6let
7 inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest;
8 inherit (pkgs.lib) concatStringsSep maintainers mapAttrs mkMerge
9 removeSuffix replaceChars singleton splitString;
10
11 /*
12 * The attrset `exporterTests` contains one attribute
13 * for each exporter test. Each of these attributes
14 * is expected to be an attrset containing:
15 *
16 * `exporterConfig`:
17 * this attribute set contains config for the exporter itself
18 *
19 * `exporterTest`
20 * this attribute set contains test instructions
21 *
22 * `metricProvider` (optional)
23 * this attribute contains additional machine config
24 *
25 * `nodeName` (optional)
26 * override an incompatible testnode name
27 *
28 * Example:
29 * exporterTests.<exporterName> = {
30 * exporterConfig = {
31 * enable = true;
32 * };
33 * metricProvider = {
34 * services.<metricProvider>.enable = true;
35 * };
36 * exporterTest = ''
37 * wait_for_unit("prometheus-<exporterName>-exporter.service")
38 * wait_for_open_port("1234")
39 * succeed("curl -sSf 'localhost:1234/metrics'")
40 * '';
41 * };
42 *
43 * # this would generate the following test config:
44 *
45 * nodes.<exporterName> = {
46 * services.prometheus.<exporterName> = {
47 * enable = true;
48 * };
49 * services.<metricProvider>.enable = true;
50 * };
51 *
52 * testScript = ''
53 * <exporterName>.start()
54 * <exporterName>.wait_for_unit("prometheus-<exporterName>-exporter.service")
55 * <exporterName>.wait_for_open_port("1234")
56 * <exporterName>.succeed("curl -sSf 'localhost:1234/metrics'")
57 * <exporterName>.shutdown()
58 * '';
59 */
60
61 exporterTests = {
62 apcupsd = {
63 exporterConfig = {
64 enable = true;
65 };
66 metricProvider = {
67 services.apcupsd.enable = true;
68 };
69 exporterTest = ''
70 wait_for_unit("apcupsd.service")
71 wait_for_open_port(3551)
72 wait_for_unit("prometheus-apcupsd-exporter.service")
73 wait_for_open_port(9162)
74 succeed("curl -sSf http://localhost:9162/metrics | grep -q 'apcupsd_info'")
75 '';
76 };
77
78 artifactory = {
79 exporterConfig = {
80 enable = true;
81 artiUsername = "artifactory-username";
82 artiPassword = "artifactory-password";
83 };
84 exporterTest = ''
85 wait_for_unit("prometheus-artifactory-exporter.service")
86 wait_for_open_port(9531)
87 succeed(
88 "curl -sSf http://localhost:9531/metrics | grep -q 'artifactory_up'"
89 )
90 '';
91 };
92
93 bind = {
94 exporterConfig = {
95 enable = true;
96 };
97 metricProvider = {
98 services.bind.enable = true;
99 services.bind.extraConfig = ''
100 statistics-channels {
101 inet 127.0.0.1 port 8053 allow { localhost; };
102 };
103 '';
104 };
105 exporterTest = ''
106 wait_for_unit("prometheus-bind-exporter.service")
107 wait_for_open_port(9119)
108 succeed(
109 "curl -sSf http://localhost:9119/metrics | grep -q 'bind_query_recursions_total 0'"
110 )
111 '';
112 };
113
114 bird = {
115 exporterConfig = {
116 enable = true;
117 };
118 metricProvider = {
119 services.bird2.enable = true;
120 services.bird2.config = ''
121 router id 127.0.0.1;
122
123 protocol kernel MyObviousTestString {
124 ipv4 {
125 import all;
126 export none;
127 };
128 }
129
130 protocol device {
131 }
132 '';
133 };
134 exporterTest = ''
135 wait_for_unit("prometheus-bird-exporter.service")
136 wait_for_open_port(9324)
137 wait_until_succeeds(
138 "curl -sSf http://localhost:9324/metrics | grep -q 'MyObviousTestString'"
139 )
140 '';
141 };
142
143 bitcoin = {
144 exporterConfig = {
145 enable = true;
146 rpcUser = "bitcoinrpc";
147 rpcPasswordFile = pkgs.writeText "password" "hunter2";
148 };
149 metricProvider = {
150 services.bitcoind.default.enable = true;
151 services.bitcoind.default.rpc.users.bitcoinrpc.passwordHMAC = "e8fe33f797e698ac258c16c8d7aadfbe$872bdb8f4d787367c26bcfd75e6c23c4f19d44a69f5d1ad329e5adf3f82710f7";
152 };
153 exporterTest = ''
154 wait_for_unit("prometheus-bitcoin-exporter.service")
155 wait_for_unit("bitcoind-default.service")
156 wait_for_open_port(9332)
157 succeed("curl -sSf http://localhost:9332/metrics | grep -q '^bitcoin_blocks '")
158 '';
159 };
160
161 blackbox = {
162 exporterConfig = {
163 enable = true;
164 configFile = pkgs.writeText "config.yml" (builtins.toJSON {
165 modules.icmp_v6 = {
166 prober = "icmp";
167 icmp.preferred_ip_protocol = "ip6";
168 };
169 });
170 };
171 exporterTest = ''
172 wait_for_unit("prometheus-blackbox-exporter.service")
173 wait_for_open_port(9115)
174 succeed(
175 "curl -sSf 'http://localhost:9115/probe?target=localhost&module=icmp_v6' | grep -q 'probe_success 1'"
176 )
177 '';
178 };
179
180 collectd = {
181 exporterConfig = {
182 enable = true;
183 extraFlags = [ "--web.collectd-push-path /collectd" ];
184 };
185 exporterTest = let postData = replaceChars [ "\n" ] [ "" ] ''
186 [{
187 "values":[23],
188 "dstypes":["gauge"],
189 "type":"gauge",
190 "interval":1000,
191 "host":"testhost",
192 "plugin":"testplugin",
193 "time":DATE
194 }]
195 ''; in
196 ''
197 wait_for_unit("prometheus-collectd-exporter.service")
198 wait_for_open_port(9103)
199 succeed(
200 'echo \'${postData}\'> /tmp/data.json'
201 )
202 succeed('sed -ie "s DATE $(date +%s) " /tmp/data.json')
203 succeed(
204 "curl -sSfH 'Content-Type: application/json' -X POST --data @/tmp/data.json localhost:9103/collectd"
205 )
206 succeed(
207 "curl -sSf localhost:9103/metrics | grep -q 'collectd_testplugin_gauge{instance=\"testhost\"} 23'"
208 )
209 '';
210 };
211
212 dnsmasq = {
213 exporterConfig = {
214 enable = true;
215 leasesPath = "/var/lib/dnsmasq/dnsmasq.leases";
216 };
217 metricProvider = {
218 services.dnsmasq.enable = true;
219 };
220 exporterTest = ''
221 wait_for_unit("prometheus-dnsmasq-exporter.service")
222 wait_for_open_port(9153)
223 succeed("curl -sSf http://localhost:9153/metrics | grep -q 'dnsmasq_leases 0'")
224 '';
225 };
226
227 # Access to WHOIS server is required to properly test this exporter, so
228 # just perform basic sanity check that the exporter is running and returns
229 # a failure.
230 domain = {
231 exporterConfig = {
232 enable = true;
233 };
234 exporterTest = ''
235 wait_for_unit("prometheus-domain-exporter.service")
236 wait_for_open_port(9222)
237 succeed(
238 "curl -sSf 'http://localhost:9222/probe?target=nixos.org' | grep -q 'domain_probe_success 0'"
239 )
240 '';
241 };
242
243 dovecot = {
244 exporterConfig = {
245 enable = true;
246 scopes = [ "global" ];
247 socketPath = "/var/run/dovecot2/old-stats";
248 user = "root"; # <- don't use user root in production
249 };
250 metricProvider = {
251 services.dovecot2.enable = true;
252 };
253 exporterTest = ''
254 wait_for_unit("prometheus-dovecot-exporter.service")
255 wait_for_open_port(9166)
256 succeed(
257 "curl -sSf http://localhost:9166/metrics | grep -q 'dovecot_up{scope=\"global\"} 1'"
258 )
259 '';
260 };
261
262 fritzbox = {
263 # TODO add proper test case
264 exporterConfig = {
265 enable = true;
266 };
267 exporterTest = ''
268 wait_for_unit("prometheus-fritzbox-exporter.service")
269 wait_for_open_port(9133)
270 succeed(
271 "curl -sSf http://localhost:9133/metrics | grep -q 'fritzbox_exporter_collect_errors 0'"
272 )
273 '';
274 };
275
276 jitsi = {
277 exporterConfig = {
278 enable = true;
279 };
280 metricProvider = {
281 systemd.services.prometheus-jitsi-exporter.after = [ "jitsi-videobridge2.service" ];
282 services.jitsi-videobridge = {
283 enable = true;
284 apis = [ "colibri" "rest" ];
285 };
286 };
287 exporterTest = ''
288 wait_for_unit("jitsi-videobridge2.service")
289 wait_for_open_port(8080)
290 wait_for_unit("prometheus-jitsi-exporter.service")
291 wait_for_open_port(9700)
292 wait_until_succeeds(
293 'journalctl -eu prometheus-jitsi-exporter.service -o cat | grep -q "key=participants"'
294 )
295 succeed("curl -sSf 'localhost:9700/metrics' | grep -q 'jitsi_participants 0'")
296 '';
297 };
298
299 json = {
300 exporterConfig = {
301 enable = true;
302 url = "http://localhost";
303 configFile = pkgs.writeText "json-exporter-conf.json" (builtins.toJSON {
304 metrics = [
305 { name = "json_test_metric"; path = "{ .test }"; }
306 ];
307 });
308 };
309 metricProvider = {
310 systemd.services.prometheus-json-exporter.after = [ "nginx.service" ];
311 services.nginx = {
312 enable = true;
313 virtualHosts.localhost.locations."/".extraConfig = ''
314 return 200 "{\"test\":1}";
315 '';
316 };
317 };
318 exporterTest = ''
319 wait_for_unit("nginx.service")
320 wait_for_open_port(80)
321 wait_for_unit("prometheus-json-exporter.service")
322 wait_for_open_port(7979)
323 succeed(
324 "curl -sSf 'localhost:7979/probe?target=http://localhost' | grep -q 'json_test_metric 1'"
325 )
326 '';
327 };
328
329 kea = {
330 exporterConfig = {
331 enable = true;
332 controlSocketPaths = [
333 "/run/kea/kea-dhcp6.sock"
334 ];
335 };
336 metricProvider = {
337 users.users.kea = {
338 isSystemUser = true;
339 };
340 users.groups.kea = {};
341
342 systemd.services.prometheus-kea-exporter.after = [ "kea-dhcp6.service" ];
343
344 systemd.services.kea-dhcp6 = let
345 configFile = pkgs.writeText "kea-dhcp6.conf" (builtins.toJSON {
346 Dhcp6 = {
347 "control-socket" = {
348 "socket-type" = "unix";
349 "socket-name" = "/run/kea/kea-dhcp6.sock";
350 };
351 };
352 });
353 in
354 {
355 after = [ "network.target" ];
356 wantedBy = [ "multi-user.target" ];
357
358 serviceConfig = {
359 DynamicUser = false;
360 User = "kea";
361 Group = "kea";
362 ExecStart = "${pkgs.kea}/bin/kea-dhcp6 -c ${configFile}";
363 StateDirectory = "kea";
364 RuntimeDirectory = "kea";
365 UMask = "0007";
366 };
367 };
368 };
369 exporterTest = ''
370 wait_for_unit("kea-dhcp6.service")
371 wait_for_file("/run/kea/kea-dhcp6.sock")
372 wait_for_unit("prometheus-kea-exporter.service")
373 wait_for_open_port(9547)
374 succeed(
375 "curl --fail localhost:9547/metrics | grep 'packets_received_total'"
376 )
377 '';
378 };
379
380 knot = {
381 exporterConfig = {
382 enable = true;
383 };
384 metricProvider = {
385 services.knot = {
386 enable = true;
387 extraArgs = [ "-v" ];
388 extraConfig = ''
389 server:
390 listen: 127.0.0.1@53
391
392 template:
393 - id: default
394 global-module: mod-stats
395 dnssec-signing: off
396 zonefile-sync: -1
397 journal-db: /var/lib/knot/journal
398 kasp-db: /var/lib/knot/kasp
399 timer-db: /var/lib/knot/timer
400 zonefile-load: difference
401 storage: ${pkgs.buildEnv {
402 name = "foo";
403 paths = [
404 (pkgs.writeTextDir "test.zone" ''
405 @ SOA ns.example.com. noc.example.com. 2019031301 86400 7200 3600000 172800
406 @ NS ns1
407 @ NS ns2
408 ns1 A 192.168.0.1
409 '')
410 ];
411 }}
412
413 mod-stats:
414 - id: custom
415 edns-presence: on
416 query-type: on
417
418 zone:
419 - domain: test
420 file: test.zone
421 module: mod-stats/custom
422 '';
423 };
424 };
425 exporterTest = ''
426 wait_for_unit("knot.service")
427 wait_for_unit("prometheus-knot-exporter.service")
428 wait_for_open_port(9433)
429 succeed("curl -sSf 'localhost:9433' | grep -q 'knot_server_zone_count 1.0'")
430 '';
431 };
432
433 keylight = {
434 # A hardware device is required to properly test this exporter, so just
435 # perform a couple of basic sanity checks that the exporter is running
436 # and requires a target, but cannot reach a specified target.
437 exporterConfig = {
438 enable = true;
439 };
440 exporterTest = ''
441 wait_for_unit("prometheus-keylight-exporter.service")
442 wait_for_open_port(9288)
443 succeed(
444 "curl -sS --write-out '%{http_code}' -o /dev/null http://localhost:9288/metrics | grep -q '400'"
445 )
446 succeed(
447 "curl -sS --write-out '%{http_code}' -o /dev/null http://localhost:9288/metrics?target=nosuchdevice | grep -q '500'"
448 )
449 '';
450 };
451
452 lnd = {
453 exporterConfig = {
454 enable = true;
455 lndTlsPath = "/var/lib/lnd/tls.cert";
456 lndMacaroonDir = "/var/lib/lnd";
457 };
458 metricProvider = {
459 systemd.services.prometheus-lnd-exporter.serviceConfig.DynamicUser = false;
460 services.bitcoind.main.enable = true;
461 services.bitcoind.main.extraConfig = ''
462 rpcauth=bitcoinrpc:e8fe33f797e698ac258c16c8d7aadfbe$872bdb8f4d787367c26bcfd75e6c23c4f19d44a69f5d1ad329e5adf3f82710f7
463 bitcoind.zmqpubrawblock=tcp://127.0.0.1:28332
464 bitcoind.zmqpubrawtx=tcp://127.0.0.1:28333
465 '';
466 systemd.services.lnd = {
467 serviceConfig.ExecStart = ''
468 ${pkgs.lnd}/bin/lnd \
469 --datadir=/var/lib/lnd \
470 --tlscertpath=/var/lib/lnd/tls.cert \
471 --tlskeypath=/var/lib/lnd/tls.key \
472 --logdir=/var/log/lnd \
473 --bitcoin.active \
474 --bitcoin.mainnet \
475 --bitcoin.node=bitcoind \
476 --bitcoind.rpcuser=bitcoinrpc \
477 --bitcoind.rpcpass=hunter2 \
478 --bitcoind.zmqpubrawblock=tcp://127.0.0.1:28332 \
479 --bitcoind.zmqpubrawtx=tcp://127.0.0.1:28333 \
480 --readonlymacaroonpath=/var/lib/lnd/readonly.macaroon
481 '';
482 serviceConfig.StateDirectory = "lnd";
483 wantedBy = [ "multi-user.target" ];
484 after = [ "network.target" ];
485 };
486 };
487 exporterTest = ''
488 wait_for_unit("lnd.service")
489 wait_for_open_port(10009)
490 wait_for_unit("prometheus-lnd-exporter.service")
491 wait_for_open_port(9092)
492 succeed("curl -sSf localhost:9092/metrics | grep -q '^promhttp_metric_handler'")
493 '';
494 };
495
496 mail = {
497 exporterConfig = {
498 enable = true;
499 configuration = {
500 monitoringInterval = "2s";
501 mailCheckTimeout = "10s";
502 servers = [{
503 name = "testserver";
504 server = "localhost";
505 port = 25;
506 from = "mail-exporter@localhost";
507 to = "mail-exporter@localhost";
508 detectionDir = "/var/spool/mail/mail-exporter/new";
509 }];
510 };
511 };
512 metricProvider = {
513 services.postfix.enable = true;
514 systemd.services.prometheus-mail-exporter = {
515 after = [ "postfix.service" ];
516 requires = [ "postfix.service" ];
517 preStart = ''
518 mkdir -p -m 0700 mail-exporter/new
519 '';
520 serviceConfig = {
521 ProtectHome = true;
522 ReadOnlyPaths = "/";
523 ReadWritePaths = "/var/spool/mail";
524 WorkingDirectory = "/var/spool/mail";
525 };
526 };
527 users.users.mailexporter.isSystemUser = true;
528 };
529 exporterTest = ''
530 wait_for_unit("postfix.service")
531 wait_for_unit("prometheus-mail-exporter.service")
532 wait_for_open_port(9225)
533 wait_until_succeeds(
534 "curl -sSf http://localhost:9225/metrics | grep -q 'mail_deliver_success{configname=\"testserver\"} 1'"
535 )
536 '';
537 };
538
539 mikrotik = {
540 exporterConfig = {
541 enable = true;
542 extraFlags = [ "-timeout=1s" ];
543 configuration = {
544 devices = [
545 {
546 name = "router";
547 address = "192.168.42.48";
548 user = "prometheus";
549 password = "shh";
550 }
551 ];
552 features = {
553 bgp = true;
554 dhcp = true;
555 dhcpl = true;
556 dhcpv6 = true;
557 health = true;
558 routes = true;
559 poe = true;
560 pools = true;
561 optics = true;
562 w60g = true;
563 wlansta = true;
564 wlanif = true;
565 monitor = true;
566 ipsec = true;
567 };
568 };
569 };
570 exporterTest = ''
571 wait_for_unit("prometheus-mikrotik-exporter.service")
572 wait_for_open_port(9436)
573 succeed(
574 "curl -sSf http://localhost:9436/metrics | grep -q 'mikrotik_scrape_collector_success{device=\"router\"} 0'"
575 )
576 '';
577 };
578
579 modemmanager = {
580 exporterConfig = {
581 enable = true;
582 refreshRate = "10s";
583 };
584 metricProvider = {
585 # ModemManager is installed when NetworkManager is enabled. Ensure it is
586 # started and is wanted by NM and the exporter to start everything up
587 # in the right order.
588 networking.networkmanager.enable = true;
589 systemd.services.ModemManager = {
590 enable = true;
591 wantedBy = [ "NetworkManager.service" "prometheus-modemmanager-exporter.service" ];
592 };
593 };
594 exporterTest = ''
595 wait_for_unit("ModemManager.service")
596 wait_for_unit("prometheus-modemmanager-exporter.service")
597 wait_for_open_port(9539)
598 succeed(
599 "curl -sSf http://localhost:9539/metrics | grep -q 'modemmanager_info'"
600 )
601 '';
602 };
603
604 nextcloud = {
605 exporterConfig = {
606 enable = true;
607 passwordFile = "/var/nextcloud-pwfile";
608 url = "http://localhost";
609 };
610 metricProvider = {
611 systemd.services.nc-pwfile =
612 let
613 passfile = (pkgs.writeText "pwfile" "snakeoilpw");
614 in
615 {
616 requiredBy = [ "prometheus-nextcloud-exporter.service" ];
617 before = [ "prometheus-nextcloud-exporter.service" ];
618 serviceConfig.ExecStart = ''
619 ${pkgs.coreutils}/bin/install -o nextcloud-exporter -m 0400 ${passfile} /var/nextcloud-pwfile
620 '';
621 };
622 services.nginx = {
623 enable = true;
624 virtualHosts."localhost" = {
625 basicAuth.nextcloud-exporter = "snakeoilpw";
626 locations."/" = {
627 root = "${pkgs.prometheus-nextcloud-exporter.src}/serverinfo/testdata";
628 tryFiles = "/negative-space.xml =404";
629 };
630 };
631 };
632 };
633 exporterTest = ''
634 wait_for_unit("nginx.service")
635 wait_for_unit("prometheus-nextcloud-exporter.service")
636 wait_for_open_port(9205)
637 succeed("curl -sSf http://localhost:9205/metrics | grep -q 'nextcloud_up 1'")
638 '';
639 };
640
641 nginx = {
642 exporterConfig = {
643 enable = true;
644 };
645 metricProvider = {
646 services.nginx = {
647 enable = true;
648 statusPage = true;
649 virtualHosts."test".extraConfig = "return 204;";
650 };
651 };
652 exporterTest = ''
653 wait_for_unit("nginx.service")
654 wait_for_unit("prometheus-nginx-exporter.service")
655 wait_for_open_port(9113)
656 succeed("curl -sSf http://localhost:9113/metrics | grep -q 'nginx_up 1'")
657 '';
658 };
659
660 nginxlog = {
661 exporterConfig = {
662 enable = true;
663 group = "nginx";
664 settings = {
665 namespaces = [
666 {
667 name = "filelogger";
668 source = {
669 files = [ "/var/log/nginx/filelogger.access.log" ];
670 };
671 }
672 {
673 name = "syslogger";
674 source = {
675 syslog = {
676 listen_address = "udp://127.0.0.1:10000";
677 format = "rfc3164";
678 tags = [ "nginx" ];
679 };
680 };
681 }
682 ];
683 };
684 };
685 metricProvider = {
686 services.nginx = {
687 enable = true;
688 httpConfig = ''
689 server {
690 listen 80;
691 server_name filelogger.local;
692 access_log /var/log/nginx/filelogger.access.log;
693 }
694 server {
695 listen 81;
696 server_name syslogger.local;
697 access_log syslog:server=127.0.0.1:10000,tag=nginx,severity=info;
698 }
699 '';
700 };
701 };
702 exporterTest = ''
703 wait_for_unit("nginx.service")
704 wait_for_unit("prometheus-nginxlog-exporter.service")
705 wait_for_open_port(9117)
706 wait_for_open_port(80)
707 wait_for_open_port(81)
708 succeed("curl http://localhost")
709 execute("sleep 1")
710 succeed(
711 "curl -sSf http://localhost:9117/metrics | grep 'filelogger_http_response_count_total' | grep -q 1"
712 )
713 succeed("curl http://localhost:81")
714 execute("sleep 1")
715 succeed(
716 "curl -sSf http://localhost:9117/metrics | grep 'syslogger_http_response_count_total' | grep -q 1"
717 )
718 '';
719 };
720
721 node = {
722 exporterConfig = {
723 enable = true;
724 };
725 exporterTest = ''
726 wait_for_unit("prometheus-node-exporter.service")
727 wait_for_open_port(9100)
728 succeed(
729 "curl -sSf http://localhost:9100/metrics | grep -q 'node_exporter_build_info{.\\+} 1'"
730 )
731 '';
732 };
733
734 openldap = {
735 exporterConfig = {
736 enable = true;
737 ldapCredentialFile = "${pkgs.writeText "exporter.yml" ''
738 ldapUser: "cn=root,dc=example"
739 ldapPass: "notapassword"
740 ''}";
741 };
742 metricProvider = {
743 services.openldap = {
744 enable = true;
745 settings.children = {
746 "cn=schema".includes = [
747 "${pkgs.openldap}/etc/schema/core.ldif"
748 "${pkgs.openldap}/etc/schema/cosine.ldif"
749 "${pkgs.openldap}/etc/schema/inetorgperson.ldif"
750 "${pkgs.openldap}/etc/schema/nis.ldif"
751 ];
752 "olcDatabase={1}mdb" = {
753 attrs = {
754 objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ];
755 olcDatabase = "{1}mdb";
756 olcDbDirectory = "/var/db/openldap";
757 olcSuffix = "dc=example";
758 olcRootDN = {
759 # cn=root,dc=example
760 base64 = "Y249cm9vdCxkYz1leGFtcGxl";
761 };
762 olcRootPW = {
763 path = "${pkgs.writeText "rootpw" "notapassword"}";
764 };
765 };
766 };
767 "olcDatabase={2}monitor".attrs = {
768 objectClass = [ "olcDatabaseConfig" ];
769 olcDatabase = "{2}monitor";
770 olcAccess = [ "to dn.subtree=cn=monitor by users read" ];
771 };
772 };
773 declarativeContents."dc=example" = ''
774 dn: dc=example
775 objectClass: domain
776 dc: example
777
778 dn: ou=users,dc=example
779 objectClass: organizationalUnit
780 ou: users
781 '';
782 };
783 };
784 exporterTest = ''
785 wait_for_unit("prometheus-openldap-exporter.service")
786 wait_for_open_port(389)
787 wait_for_open_port(9330)
788 wait_until_succeeds(
789 "curl -sSf http://localhost:9330/metrics | grep -q 'openldap_scrape{result=\"ok\"} 1'"
790 )
791 '';
792 };
793
794 openvpn = {
795 exporterConfig = {
796 enable = true;
797 group = "openvpn";
798 statusPaths = [ "/run/openvpn-test" ];
799 };
800 metricProvider = {
801 users.groups.openvpn = { };
802 services.openvpn.servers.test = {
803 config = ''
804 dev tun
805 status /run/openvpn-test
806 status-version 3
807 '';
808 up = "chmod g+r /run/openvpn-test";
809 };
810 systemd.services."openvpn-test".serviceConfig.Group = "openvpn";
811 };
812 exporterTest = ''
813 wait_for_unit("openvpn-test.service")
814 wait_for_unit("prometheus-openvpn-exporter.service")
815 succeed("curl -sSf http://localhost:9176/metrics | grep -q 'openvpn_up{.*} 1'")
816 '';
817 };
818
819 postfix = {
820 exporterConfig = {
821 enable = true;
822 };
823 metricProvider = {
824 services.postfix.enable = true;
825 };
826 exporterTest = ''
827 wait_for_unit("prometheus-postfix-exporter.service")
828 wait_for_file("/var/lib/postfix/queue/public/showq")
829 wait_for_open_port(9154)
830 succeed(
831 "curl -sSf http://localhost:9154/metrics | grep -q 'postfix_smtpd_connects_total 0'"
832 )
833 succeed("curl -sSf http://localhost:9154/metrics | grep -q 'postfix_up{.*} 1'")
834 '';
835 };
836
837 postgres = {
838 exporterConfig = {
839 enable = true;
840 runAsLocalSuperUser = true;
841 };
842 metricProvider = {
843 services.postgresql.enable = true;
844 };
845 exporterTest = ''
846 wait_for_unit("prometheus-postgres-exporter.service")
847 wait_for_open_port(9187)
848 wait_for_unit("postgresql.service")
849 succeed(
850 "curl -sSf http://localhost:9187/metrics | grep -q 'pg_exporter_last_scrape_error 0'"
851 )
852 succeed("curl -sSf http://localhost:9187/metrics | grep -q 'pg_up 1'")
853 systemctl("stop postgresql.service")
854 succeed(
855 "curl -sSf http://localhost:9187/metrics | grep -qv 'pg_exporter_last_scrape_error 0'"
856 )
857 succeed("curl -sSf http://localhost:9187/metrics | grep -q 'pg_up 0'")
858 systemctl("start postgresql.service")
859 wait_for_unit("postgresql.service")
860 succeed(
861 "curl -sSf http://localhost:9187/metrics | grep -q 'pg_exporter_last_scrape_error 0'"
862 )
863 succeed("curl -sSf http://localhost:9187/metrics | grep -q 'pg_up 1'")
864 '';
865 };
866
867 py-air-control = {
868 nodeName = "py_air_control";
869 exporterConfig = {
870 enable = true;
871 deviceHostname = "127.0.0.1";
872 };
873 exporterTest = ''
874 wait_for_unit("prometheus-py-air-control-exporter.service")
875 wait_for_open_port(9896)
876 succeed(
877 "curl -sSf http://localhost:9896/metrics | grep -q 'py_air_control_sampling_error_total'"
878 )
879 '';
880 };
881
882 redis = {
883 exporterConfig = {
884 enable = true;
885 };
886 metricProvider.services.redis.enable = true;
887 exporterTest = ''
888 wait_for_unit("redis.service")
889 wait_for_unit("prometheus-redis-exporter.service")
890 wait_for_open_port(6379)
891 wait_for_open_port(9121)
892 wait_until_succeeds("curl -sSf localhost:9121/metrics | grep -q 'redis_up 1'")
893 '';
894 };
895
896 rspamd = {
897 exporterConfig = {
898 enable = true;
899 };
900 metricProvider = {
901 services.rspamd.enable = true;
902 virtualisation.memorySize = 1024;
903 };
904 exporterTest = ''
905 wait_for_unit("rspamd.service")
906 wait_for_unit("prometheus-rspamd-exporter.service")
907 wait_for_open_port(11334)
908 wait_for_open_port(7980)
909 wait_until_succeeds(
910 "curl -sSf 'localhost:7980/probe?target=http://localhost:11334/stat' | grep -q 'rspamd_scanned{host=\"rspamd\"} 0'"
911 )
912 '';
913 };
914
915 rtl_433 = {
916 exporterConfig = {
917 enable = true;
918 };
919 metricProvider = {
920 # Mock rtl_433 binary to return a dummy metric stream.
921 nixpkgs.overlays = [
922 (self: super: {
923 rtl_433 = self.runCommand "rtl_433" { } ''
924 mkdir -p "$out/bin"
925 cat <<EOF > "$out/bin/rtl_433"
926 #!/bin/sh
927 while true; do
928 printf '{"time" : "2020-04-26 13:37:42", "model" : "zopieux", "id" : 55, "channel" : 3, "temperature_C" : 18.000}\n'
929 sleep 4
930 done
931 EOF
932 chmod +x "$out/bin/rtl_433"
933 '';
934 })
935 ];
936 };
937 exporterTest = ''
938 wait_for_unit("prometheus-rtl_433-exporter.service")
939 wait_for_open_port(9550)
940 wait_until_succeeds(
941 "curl -sSf localhost:9550/metrics | grep -q '{}'".format(
942 'rtl_433_temperature_celsius{channel="3",id="55",location="",model="zopieux"} 18'
943 )
944 )
945 '';
946 };
947
948 smokeping = {
949 exporterConfig = {
950 enable = true;
951 hosts = [ "127.0.0.1" ];
952 };
953 exporterTest = ''
954 wait_for_unit("prometheus-smokeping-exporter.service")
955 wait_for_open_port(9374)
956 wait_until_succeeds(
957 "curl -sSf localhost:9374/metrics | grep '{}' | grep -qv ' 0$'".format(
958 'smokeping_requests_total{host="127.0.0.1",ip="127.0.0.1"} '
959 )
960 )
961 wait_until_succeeds(
962 "curl -sSf localhost:9374/metrics | grep -q '{}'".format(
963 'smokeping_response_ttl{host="127.0.0.1",ip="127.0.0.1"}'
964 )
965 )
966 '';
967 };
968
969 snmp = {
970 exporterConfig = {
971 enable = true;
972 configuration.default = {
973 version = 2;
974 auth.community = "public";
975 };
976 };
977 exporterTest = ''
978 wait_for_unit("prometheus-snmp-exporter.service")
979 wait_for_open_port(9116)
980 succeed("curl -sSf localhost:9116/metrics | grep -q 'snmp_request_errors_total 0'")
981 '';
982 };
983
984 sql = {
985 exporterConfig = {
986 configuration.jobs.points = {
987 interval = "1m";
988 connections = [
989 "postgres://prometheus-sql-exporter@/data?host=/run/postgresql&sslmode=disable"
990 ];
991 queries = {
992 points = {
993 labels = [ "name" ];
994 help = "Amount of points accumulated per person";
995 values = [ "amount" ];
996 query = "SELECT SUM(amount) as amount, name FROM points GROUP BY name";
997 };
998 };
999 };
1000 enable = true;
1001 user = "prometheus-sql-exporter";
1002 };
1003 metricProvider = {
1004 services.postgresql = {
1005 enable = true;
1006 initialScript = builtins.toFile "init.sql" ''
1007 CREATE DATABASE data;
1008 \c data;
1009 CREATE TABLE points (amount INT, name TEXT);
1010 INSERT INTO points(amount, name) VALUES (1, 'jack');
1011 INSERT INTO points(amount, name) VALUES (2, 'jill');
1012 INSERT INTO points(amount, name) VALUES (3, 'jack');
1013
1014 CREATE USER "prometheus-sql-exporter";
1015 GRANT ALL PRIVILEGES ON DATABASE data TO "prometheus-sql-exporter";
1016 GRANT SELECT ON points TO "prometheus-sql-exporter";
1017 '';
1018 };
1019 systemd.services.prometheus-sql-exporter.after = [ "postgresql.service" ];
1020 };
1021 exporterTest = ''
1022 wait_for_unit("prometheus-sql-exporter.service")
1023 wait_for_open_port(9237)
1024 succeed("curl http://localhost:9237/metrics | grep -c 'sql_points{' | grep -q 2")
1025 '';
1026 };
1027
1028 surfboard = {
1029 exporterConfig = {
1030 enable = true;
1031 modemAddress = "localhost";
1032 };
1033 metricProvider = {
1034 systemd.services.prometheus-surfboard-exporter.after = [ "nginx.service" ];
1035 services.nginx = {
1036 enable = true;
1037 virtualHosts.localhost.locations."/cgi-bin/status".extraConfig = ''
1038 return 204;
1039 '';
1040 };
1041 };
1042 exporterTest = ''
1043 wait_for_unit("nginx.service")
1044 wait_for_open_port(80)
1045 wait_for_unit("prometheus-surfboard-exporter.service")
1046 wait_for_open_port(9239)
1047 succeed("curl -sSf localhost:9239/metrics | grep -q 'surfboard_up 1'")
1048 '';
1049 };
1050
1051 systemd = {
1052 exporterConfig = {
1053 enable = true;
1054 };
1055 metricProvider = { };
1056 exporterTest = ''
1057 wait_for_unit("prometheus-systemd-exporter.service")
1058 wait_for_open_port(9558)
1059 succeed(
1060 "curl -sSf localhost:9558/metrics | grep -q '{}'".format(
1061 'systemd_unit_state{name="basic.target",state="active",type="target"} 1'
1062 )
1063 )
1064 '';
1065 };
1066
1067 tor = {
1068 exporterConfig = {
1069 enable = true;
1070 };
1071 metricProvider = {
1072 # Note: this does not connect the test environment to the Tor network.
1073 # Client, relay, bridge or exit connectivity are disabled by default.
1074 services.tor.enable = true;
1075 services.tor.settings.ControlPort = 9051;
1076 };
1077 exporterTest = ''
1078 wait_for_unit("tor.service")
1079 wait_for_open_port(9051)
1080 wait_for_unit("prometheus-tor-exporter.service")
1081 wait_for_open_port(9130)
1082 succeed("curl -sSf localhost:9130/metrics | grep -q 'tor_version{.\\+} 1'")
1083 '';
1084 };
1085
1086 unifi-poller = {
1087 nodeName = "unifi_poller";
1088 exporterConfig.enable = true;
1089 exporterConfig.controllers = [{ }];
1090 exporterTest = ''
1091 wait_for_unit("prometheus-unifi-poller-exporter.service")
1092 wait_for_open_port(9130)
1093 succeed(
1094 "curl -sSf localhost:9130/metrics | grep -q 'unifipoller_build_info{.\\+} 1'"
1095 )
1096 '';
1097 };
1098
1099 unbound = {
1100 exporterConfig = {
1101 enable = true;
1102 fetchType = "uds";
1103 controlInterface = "/run/unbound/unbound.ctl";
1104 };
1105 metricProvider = {
1106 services.unbound = {
1107 enable = true;
1108 localControlSocketPath = "/run/unbound/unbound.ctl";
1109 };
1110 systemd.services.prometheus-unbound-exporter.serviceConfig = {
1111 SupplementaryGroups = [ "unbound" ];
1112 };
1113 };
1114 exporterTest = ''
1115 wait_for_unit("unbound.service")
1116 wait_for_unit("prometheus-unbound-exporter.service")
1117 wait_for_open_port(9167)
1118 succeed("curl -sSf localhost:9167/metrics | grep -q 'unbound_up 1'")
1119 '';
1120 };
1121
1122 varnish = {
1123 exporterConfig = {
1124 enable = true;
1125 instance = "/var/spool/varnish/varnish";
1126 group = "varnish";
1127 };
1128 metricProvider = {
1129 systemd.services.prometheus-varnish-exporter.after = [
1130 "varnish.service"
1131 ];
1132 services.varnish = {
1133 enable = true;
1134 config = ''
1135 vcl 4.0;
1136 backend default {
1137 .host = "127.0.0.1";
1138 .port = "80";
1139 }
1140 '';
1141 };
1142 };
1143 exporterTest = ''
1144 wait_for_unit("prometheus-varnish-exporter.service")
1145 wait_for_open_port(6081)
1146 wait_for_open_port(9131)
1147 succeed("curl -sSf http://localhost:9131/metrics | grep -q 'varnish_up 1'")
1148 '';
1149 };
1150
1151 wireguard = let snakeoil = import ./wireguard/snakeoil-keys.nix; in
1152 {
1153 exporterConfig.enable = true;
1154 metricProvider = {
1155 networking.wireguard.interfaces.wg0 = {
1156 ips = [ "10.23.42.1/32" "fc00::1/128" ];
1157 listenPort = 23542;
1158
1159 inherit (snakeoil.peer0) privateKey;
1160
1161 peers = singleton {
1162 allowedIPs = [ "10.23.42.2/32" "fc00::2/128" ];
1163
1164 inherit (snakeoil.peer1) publicKey;
1165 };
1166 };
1167 systemd.services.prometheus-wireguard-exporter.after = [ "wireguard-wg0.service" ];
1168 };
1169 exporterTest = ''
1170 wait_for_unit("prometheus-wireguard-exporter.service")
1171 wait_for_open_port(9586)
1172 wait_until_succeeds(
1173 "curl -sSf http://localhost:9586/metrics | grep '${snakeoil.peer1.publicKey}'"
1174 )
1175 '';
1176 };
1177 };
1178in
1179mapAttrs
1180 (exporter: testConfig: (makeTest (
1181 let
1182 nodeName = testConfig.nodeName or exporter;
1183
1184 in
1185 {
1186 name = "prometheus-${exporter}-exporter";
1187
1188 nodes.${nodeName} = mkMerge [{
1189 services.prometheus.exporters.${exporter} = testConfig.exporterConfig;
1190 } testConfig.metricProvider or { }];
1191
1192 testScript = ''
1193 ${nodeName}.start()
1194 ${concatStringsSep "\n" (map (line:
1195 if (builtins.substring 0 1 line == " " || builtins.substring 0 1 line == ")")
1196 then line
1197 else "${nodeName}.${line}"
1198 ) (splitString "\n" (removeSuffix "\n" testConfig.exporterTest)))}
1199 ${nodeName}.shutdown()
1200 '';
1201
1202 meta = with maintainers; {
1203 maintainers = [ willibutz elseym ];
1204 };
1205 }
1206 )))
1207 exporterTests