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 v6 monitoring service
55 '';
56 default = false;
57 type = types.bool;
58 };
59
60 package = mkOption {
61 default = pkgs.datadog-agent;
62 defaultText = "pkgs.datadog-agent";
63 description = ''
64 Which DataDog v6 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 = {
139 ntp = (pythonPackages: [ pythonPackages.ntplib ]);
140 };
141 };
142
143 extraConfig = mkOption {
144 default = {};
145 type = types.attrs;
146 description = ''
147 Extra configuration options that will be merged into the
148 main config file <filename>datadog.yaml</filename>.
149 '';
150 };
151
152 enableLiveProcessCollection = mkOption {
153 description = ''
154 Whether to enable the live process collection agent.
155 '';
156 default = false;
157 type = types.bool;
158 };
159
160 enableTraceAgent = mkOption {
161 description = ''
162 Whether to enable the trace agent.
163 '';
164 default = false;
165 type = types.bool;
166 };
167
168 checks = mkOption {
169 description = ''
170 Configuration for all Datadog checks. Keys of this attribute
171 set will be used as the name of the check to create the
172 appropriate configuration in `conf.d/$check.d/conf.yaml`.
173
174 The configuration is converted into JSON from the plain Nix
175 language configuration, meaning that you should write
176 configuration adhering to Datadog's documentation - but in Nix
177 language.
178
179 Refer to the implementation of this module (specifically the
180 definition of `defaultChecks`) for an example.
181
182 Note: The 'disk' and 'network' check are configured in
183 separate options because they exist by default. Attempting to
184 override their configuration here will have no effect.
185 '';
186
187 example = {
188 http_check = {
189 init_config = null; # sic!
190 instances = [
191 {
192 name = "some-service";
193 url = "http://localhost:1337/healthz";
194 tags = [ "some-service" ];
195 }
196 ];
197 };
198 };
199
200 default = {};
201
202 # sic! The structure of the values is up to the check, so we can
203 # not usefully constrain the type further.
204 type = with types; attrsOf attrs;
205 };
206
207 diskCheck = mkOption {
208 description = "Disk check config";
209 type = types.attrs;
210 default = {
211 init_config = {};
212 instances = [ { use_mount = "false"; } ];
213 };
214 };
215
216 networkCheck = mkOption {
217 description = "Network check config";
218 type = types.attrs;
219 default = {
220 init_config = {};
221 # Network check only supports one configured instance
222 instances = [ { collect_connection_state = false;
223 excluded_interfaces = [ "lo" "lo0" ]; } ];
224 };
225 };
226 };
227 config = mkIf cfg.enable {
228 environment.systemPackages = [ datadogPkg pkgs.sysstat pkgs.procps pkgs.iproute2 ];
229
230 users.users.datadog = {
231 description = "Datadog Agent User";
232 uid = config.ids.uids.datadog;
233 group = "datadog";
234 home = "/var/log/datadog/";
235 createHome = true;
236 };
237
238 users.groups.datadog.gid = config.ids.gids.datadog;
239
240 systemd.services = let
241 makeService = attrs: recursiveUpdate {
242 path = [ datadogPkg pkgs.python pkgs.sysstat pkgs.procps pkgs.iproute2 ];
243 wantedBy = [ "multi-user.target" ];
244 serviceConfig = {
245 User = "datadog";
246 Group = "datadog";
247 Restart = "always";
248 RestartSec = 2;
249 };
250 restartTriggers = [ datadogPkg ] ++ map (x: x.source) (attrValues etcfiles);
251 } attrs;
252 in {
253 datadog-agent = makeService {
254 description = "Datadog agent monitor";
255 preStart = ''
256 chown -R datadog: /etc/datadog-agent
257 rm -f /etc/datadog-agent/auth_token
258 '';
259 script = ''
260 export DD_API_KEY=$(head -n 1 ${cfg.apiKeyFile})
261 exec ${datadogPkg}/bin/agent run -c /etc/datadog-agent/datadog.yaml
262 '';
263 serviceConfig.PermissionsStartOnly = true;
264 };
265
266 dd-jmxfetch = lib.mkIf (lib.hasAttr "jmx" cfg.checks) (makeService {
267 description = "Datadog JMX Fetcher";
268 path = [ datadogPkg pkgs.python pkgs.sysstat pkgs.procps pkgs.jdk ];
269 serviceConfig.ExecStart = "${datadogPkg}/bin/dd-jmxfetch";
270 });
271
272 datadog-process-agent = lib.mkIf cfg.enableLiveProcessCollection (makeService {
273 description = "Datadog Live Process Agent";
274 path = [ ];
275 script = ''
276 export DD_API_KEY=$(head -n 1 ${cfg.apiKeyFile})
277 ${pkgs.datadog-process-agent}/bin/agent --config /etc/datadog-agent/datadog.yaml
278 '';
279 });
280
281 datadog-trace-agent = lib.mkIf cfg.enableTraceAgent (makeService {
282 description = "Datadog Trace Agent";
283 path = [ ];
284 script = ''
285 export DD_API_KEY=$(head -n 1 ${cfg.apiKeyFile})
286 ${datadogPkg}/bin/trace-agent -config /etc/datadog-agent/datadog.yaml
287 '';
288 });
289
290 };
291
292 environment.etc = etcfiles;
293 };
294}