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