1{ config, pkgs, lib, ... }:
2
3with lib;
4
5let
6 cfg = config.services.prometheus.alertmanager;
7 mkConfigFile = pkgs.writeText "alertmanager.yml" (builtins.toJSON cfg.configuration);
8
9 checkedConfig = file: pkgs.runCommand "checked-config" { buildInputs = [ cfg.package ]; } ''
10 ln -s ${file} $out
11 amtool check-config $out
12 '';
13
14 alertmanagerYml = let
15 yml = if cfg.configText != null then
16 pkgs.writeText "alertmanager.yml" cfg.configText
17 else mkConfigFile;
18 in checkedConfig yml;
19
20 cmdlineArgs = cfg.extraFlags ++ [
21 "--config.file /tmp/alert-manager-substituted.yaml"
22 "--web.listen-address ${cfg.listenAddress}:${toString cfg.port}"
23 "--log.level ${cfg.logLevel}"
24 "--storage.path /var/lib/alertmanager"
25 (toString (map (peer: "--cluster.peer ${peer}:9094") cfg.clusterPeers))
26 ] ++ (optional (cfg.webExternalUrl != null)
27 "--web.external-url ${cfg.webExternalUrl}"
28 ) ++ (optional (cfg.logFormat != null)
29 "--log.format ${cfg.logFormat}"
30 );
31in {
32 imports = [
33 (mkRemovedOptionModule [ "services" "prometheus" "alertmanager" "user" ] "The alertmanager service is now using systemd's DynamicUser mechanism which obviates a user setting.")
34 (mkRemovedOptionModule [ "services" "prometheus" "alertmanager" "group" ] "The alertmanager service is now using systemd's DynamicUser mechanism which obviates a group setting.")
35 (mkRemovedOptionModule [ "services" "prometheus" "alertmanagerURL" ] ''
36 Due to incompatibility, the alertmanagerURL option has been removed,
37 please use 'services.prometheus2.alertmanagers' instead.
38 '')
39 ];
40
41 options = {
42 services.prometheus.alertmanager = {
43 enable = mkEnableOption (lib.mdDoc "Prometheus Alertmanager");
44
45 package = mkOption {
46 type = types.package;
47 default = pkgs.prometheus-alertmanager;
48 defaultText = literalExpression "pkgs.alertmanager";
49 description = lib.mdDoc ''
50 Package that should be used for alertmanager.
51 '';
52 };
53
54 configuration = mkOption {
55 type = types.nullOr types.attrs;
56 default = null;
57 description = lib.mdDoc ''
58 Alertmanager configuration as nix attribute set.
59 '';
60 };
61
62 configText = mkOption {
63 type = types.nullOr types.lines;
64 default = null;
65 description = lib.mdDoc ''
66 Alertmanager configuration as YAML text. If non-null, this option
67 defines the text that is written to alertmanager.yml. If null, the
68 contents of alertmanager.yml is generated from the structured config
69 options.
70 '';
71 };
72
73 logFormat = mkOption {
74 type = types.nullOr types.str;
75 default = null;
76 description = lib.mdDoc ''
77 If set use a syslog logger or JSON logging.
78 '';
79 };
80
81 logLevel = mkOption {
82 type = types.enum ["debug" "info" "warn" "error" "fatal"];
83 default = "warn";
84 description = lib.mdDoc ''
85 Only log messages with the given severity or above.
86 '';
87 };
88
89 webExternalUrl = mkOption {
90 type = types.nullOr types.str;
91 default = null;
92 description = lib.mdDoc ''
93 The URL under which Alertmanager is externally reachable (for example, if Alertmanager is served via a reverse proxy).
94 Used for generating relative and absolute links back to Alertmanager itself.
95 If the URL has a path portion, it will be used to prefix all HTTP endoints served by Alertmanager.
96 If omitted, relevant URL components will be derived automatically.
97 '';
98 };
99
100 listenAddress = mkOption {
101 type = types.str;
102 default = "";
103 description = lib.mdDoc ''
104 Address to listen on for the web interface and API. Empty string will listen on all interfaces.
105 "localhost" will listen on 127.0.0.1 (but not ::1).
106 '';
107 };
108
109 port = mkOption {
110 type = types.int;
111 default = 9093;
112 description = lib.mdDoc ''
113 Port to listen on for the web interface and API.
114 '';
115 };
116
117 openFirewall = mkOption {
118 type = types.bool;
119 default = false;
120 description = lib.mdDoc ''
121 Open port in firewall for incoming connections.
122 '';
123 };
124
125 clusterPeers = mkOption {
126 type = types.listOf types.str;
127 default = [];
128 description = lib.mdDoc ''
129 Initial peers for HA cluster.
130 '';
131 };
132
133 extraFlags = mkOption {
134 type = types.listOf types.str;
135 default = [];
136 description = lib.mdDoc ''
137 Extra commandline options when launching the Alertmanager.
138 '';
139 };
140
141 environmentFile = mkOption {
142 type = types.nullOr types.path;
143 default = null;
144 example = "/root/alertmanager.env";
145 description = lib.mdDoc ''
146 File to load as environment file. Environment variables
147 from this file will be interpolated into the config file
148 using envsubst with this syntax:
149 `$ENVIRONMENT ''${VARIABLE}`
150 '';
151 };
152 };
153 };
154
155 config = mkMerge [
156 (mkIf cfg.enable {
157 assertions = singleton {
158 assertion = cfg.configuration != null || cfg.configText != null;
159 message = "Can not enable alertmanager without a configuration. "
160 + "Set either the `configuration` or `configText` attribute.";
161 };
162 })
163 (mkIf cfg.enable {
164 networking.firewall.allowedTCPPorts = optional cfg.openFirewall cfg.port;
165
166 systemd.services.alertmanager = {
167 wantedBy = [ "multi-user.target" ];
168 after = [ "network-online.target" ];
169 preStart = ''
170 ${lib.getBin pkgs.envsubst}/bin/envsubst -o "/tmp/alert-manager-substituted.yaml" \
171 -i "${alertmanagerYml}"
172 '';
173 serviceConfig = {
174 Restart = "always";
175 StateDirectory = "alertmanager";
176 DynamicUser = true; # implies PrivateTmp
177 EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile;
178 WorkingDirectory = "/tmp";
179 ExecStart = "${cfg.package}/bin/alertmanager" +
180 optionalString (length cmdlineArgs != 0) (" \\\n " +
181 concatStringsSep " \\\n " cmdlineArgs);
182 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
183 };
184 };
185 })
186 ];
187}