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