1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.datadog-agent;
7
8 ddConf = {
9 skip_ssl_validation = false;
10 confd_path = "/etc/datadog-agent/conf.d";
11 additional_checksd = "/etc/datadog-agent/checks.d";
12 use_dogstatsd = true;
13 }
14 // optionalAttrs (cfg.logLevel != null) { log_level = cfg.logLevel; }
15 // optionalAttrs (cfg.hostname != null) { inherit (cfg) hostname; }
16 // optionalAttrs (cfg.ddUrl != null) { dd_url = cfg.ddUrl; }
17 // optionalAttrs (cfg.site != null) { site = cfg.site; }
18 // optionalAttrs (cfg.tags != null ) { tags = concatStringsSep ", " cfg.tags; }
19 // optionalAttrs (cfg.enableLiveProcessCollection) { process_config = { enabled = "true"; }; }
20 // optionalAttrs (cfg.enableTraceAgent) { apm_config = { enabled = true; }; }
21 // cfg.extraConfig;
22
23 # Generate Datadog configuration files for each configured checks.
24 # This works because check configurations have predictable paths,
25 # and because JSON is a valid subset of YAML.
26 makeCheckConfigs = entries: mapAttrs' (name: conf: {
27 name = "datadog-agent/conf.d/${name}.d/conf.yaml";
28 value.source = pkgs.writeText "${name}-check-conf.yaml" (builtins.toJSON conf);
29 }) entries;
30
31 defaultChecks = {
32 disk = cfg.diskCheck;
33 network = cfg.networkCheck;
34 };
35
36 # Assemble all check configurations and the top-level agent
37 # configuration.
38 etcfiles = with pkgs; with builtins;
39 { "datadog-agent/datadog.yaml" = {
40 source = writeText "datadog.yaml" (toJSON ddConf);
41 };
42 } // makeCheckConfigs (cfg.checks // defaultChecks);
43
44 # Apply the configured extraIntegrations to the provided agent
45 # package. See the documentation of `dd-agent/integrations-core.nix`
46 # for detailed information on this.
47 datadogPkg = cfg.package.override {
48 pythonPackages = pkgs.datadog-integrations-core cfg.extraIntegrations;
49 };
50in {
51 options.services.datadog-agent = {
52 enable = mkOption {
53 description = ''
54 Whether to enable the datadog-agent v7 monitoring service
55 '';
56 default = false;
57 type = types.bool;
58 };
59
60 package = mkOption {
61 default = pkgs.datadog-agent;
62 defaultText = literalExpression "pkgs.datadog-agent";
63 description = ''
64 Which DataDog v7 agent package to use. Note that the provided
65 package is expected to have an overridable `pythonPackages`-attribute
66 which configures the Python environment with the Datadog
67 checks.
68 '';
69 type = types.package;
70 };
71
72 apiKeyFile = mkOption {
73 description = ''
74 Path to a file containing the Datadog API key to associate the
75 agent with your account.
76 '';
77 example = "/run/keys/datadog_api_key";
78 type = types.path;
79 };
80
81 ddUrl = mkOption {
82 description = ''
83 Custom dd_url to configure the agent with. Useful if traffic to datadog
84 needs to go through a proxy.
85 Don't use this to point to another datadog site (EU) - use site instead.
86 '';
87 default = null;
88 example = "http://haproxy.example.com:3834";
89 type = types.nullOr types.str;
90 };
91
92 site = mkOption {
93 description = ''
94 The datadog site to point the agent towards.
95 Set to datadoghq.eu to point it to their EU site.
96 '';
97 default = null;
98 example = "datadoghq.eu";
99 type = types.nullOr types.str;
100 };
101
102 tags = mkOption {
103 description = "The tags to mark this Datadog agent";
104 example = [ "test" "service" ];
105 default = null;
106 type = types.nullOr (types.listOf types.str);
107 };
108
109 hostname = mkOption {
110 description = "The hostname to show in the Datadog dashboard (optional)";
111 default = null;
112 example = "mymachine.mydomain";
113 type = types.nullOr types.str;
114 };
115
116 logLevel = mkOption {
117 description = "Logging verbosity.";
118 default = null;
119 type = types.nullOr (types.enum ["DEBUG" "INFO" "WARN" "ERROR"]);
120 };
121
122 extraIntegrations = mkOption {
123 default = {};
124 type = types.attrs;
125
126 description = ''
127 Extra integrations from the Datadog core-integrations
128 repository that should be built and included.
129
130 By default the included integrations are disk, mongo, network,
131 nginx and postgres.
132
133 To include additional integrations the name of the derivation
134 and a function to filter its dependencies from the Python
135 package set must be provided.
136 '';
137
138 example = literalExpression ''
139 {
140 ntp = pythonPackages: [ pythonPackages.ntplib ];
141 }
142 '';
143 };
144
145 extraConfig = mkOption {
146 default = {};
147 type = types.attrs;
148 description = ''
149 Extra configuration options that will be merged into the
150 main config file <filename>datadog.yaml</filename>.
151 '';
152 };
153
154 enableLiveProcessCollection = mkOption {
155 description = ''
156 Whether to enable the live process collection agent.
157 '';
158 default = false;
159 type = types.bool;
160 };
161
162 enableTraceAgent = mkOption {
163 description = ''
164 Whether to enable the trace agent.
165 '';
166 default = false;
167 type = types.bool;
168 };
169
170 checks = mkOption {
171 description = ''
172 Configuration for all Datadog checks. Keys of this attribute
173 set will be used as the name of the check to create the
174 appropriate configuration in `conf.d/$check.d/conf.yaml`.
175
176 The configuration is converted into JSON from the plain Nix
177 language configuration, meaning that you should write
178 configuration adhering to Datadog's documentation - but in Nix
179 language.
180
181 Refer to the implementation of this module (specifically the
182 definition of `defaultChecks`) for an example.
183
184 Note: The 'disk' and 'network' check are configured in
185 separate options because they exist by default. Attempting to
186 override their configuration here will have no effect.
187 '';
188
189 example = {
190 http_check = {
191 init_config = null; # sic!
192 instances = [
193 {
194 name = "some-service";
195 url = "http://localhost:1337/healthz";
196 tags = [ "some-service" ];
197 }
198 ];
199 };
200 };
201
202 default = {};
203
204 # sic! The structure of the values is up to the check, so we can
205 # not usefully constrain the type further.
206 type = with types; attrsOf attrs;
207 };
208
209 diskCheck = mkOption {
210 description = "Disk check config";
211 type = types.attrs;
212 default = {
213 init_config = {};
214 instances = [ { use_mount = "false"; } ];
215 };
216 };
217
218 networkCheck = mkOption {
219 description = "Network check config";
220 type = types.attrs;
221 default = {
222 init_config = {};
223 # Network check only supports one configured instance
224 instances = [ { collect_connection_state = false;
225 excluded_interfaces = [ "lo" "lo0" ]; } ];
226 };
227 };
228 };
229 config = mkIf cfg.enable {
230 environment.systemPackages = [ datadogPkg pkgs.sysstat pkgs.procps pkgs.iproute2 ];
231
232 users.users.datadog = {
233 description = "Datadog Agent User";
234 uid = config.ids.uids.datadog;
235 group = "datadog";
236 home = "/var/log/datadog/";
237 createHome = true;
238 };
239
240 users.groups.datadog.gid = config.ids.gids.datadog;
241
242 systemd.services = let
243 makeService = attrs: recursiveUpdate {
244 path = [ datadogPkg pkgs.python pkgs.sysstat pkgs.procps pkgs.iproute2 ];
245 wantedBy = [ "multi-user.target" ];
246 serviceConfig = {
247 User = "datadog";
248 Group = "datadog";
249 Restart = "always";
250 RestartSec = 2;
251 };
252 restartTriggers = [ datadogPkg ] ++ map (x: x.source) (attrValues etcfiles);
253 } attrs;
254 in {
255 datadog-agent = makeService {
256 description = "Datadog agent monitor";
257 preStart = ''
258 chown -R datadog: /etc/datadog-agent
259 rm -f /etc/datadog-agent/auth_token
260 '';
261 script = ''
262 export DD_API_KEY=$(head -n 1 ${cfg.apiKeyFile})
263 exec ${datadogPkg}/bin/agent run -c /etc/datadog-agent/datadog.yaml
264 '';
265 serviceConfig.PermissionsStartOnly = true;
266 };
267
268 dd-jmxfetch = lib.mkIf (lib.hasAttr "jmx" cfg.checks) (makeService {
269 description = "Datadog JMX Fetcher";
270 path = [ datadogPkg pkgs.python pkgs.sysstat pkgs.procps pkgs.jdk ];
271 serviceConfig.ExecStart = "${datadogPkg}/bin/dd-jmxfetch";
272 });
273
274 datadog-process-agent = lib.mkIf cfg.enableLiveProcessCollection (makeService {
275 description = "Datadog Live Process Agent";
276 path = [ ];
277 script = ''
278 export DD_API_KEY=$(head -n 1 ${cfg.apiKeyFile})
279 ${pkgs.datadog-process-agent}/bin/process-agent --config /etc/datadog-agent/datadog.yaml
280 '';
281 });
282
283 datadog-trace-agent = lib.mkIf cfg.enableTraceAgent (makeService {
284 description = "Datadog Trace Agent";
285 path = [ ];
286 script = ''
287 export DD_API_KEY=$(head -n 1 ${cfg.apiKeyFile})
288 ${datadogPkg}/bin/trace-agent -config /etc/datadog-agent/datadog.yaml
289 '';
290 });
291
292 };
293
294 environment.etc = etcfiles;
295 };
296}