1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.services.statsd;
8
9 isBuiltinBackend = name:
10 builtins.elem name [ "graphite" "console" "repeater" ];
11
12 backendsToPackages = let
13 mkMap = list: name:
14 if isBuiltinBackend name then list
15 else list ++ [ pkgs.nodePackages.${name} ];
16 in foldl mkMap [];
17
18 configFile = pkgs.writeText "statsd.conf" ''
19 {
20 address: "${cfg.listenAddress}",
21 port: "${toString cfg.port}",
22 mgmt_address: "${cfg.mgmt_address}",
23 mgmt_port: "${toString cfg.mgmt_port}",
24 backends: [${
25 concatMapStringsSep "," (name:
26 if (isBuiltinBackend name)
27 then ''"./backends/${name}"''
28 else ''"${name}"''
29 ) cfg.backends}],
30 ${optionalString (cfg.graphiteHost!=null) ''graphiteHost: "${cfg.graphiteHost}",''}
31 ${optionalString (cfg.graphitePort!=null) ''graphitePort: "${toString cfg.graphitePort}",''}
32 console: {
33 prettyprint: false
34 },
35 log: {
36 backend: "stdout"
37 },
38 automaticConfigReload: false${optionalString (cfg.extraConfig != null) ","}
39 ${cfg.extraConfig}
40 }
41 '';
42
43 deps = pkgs.buildEnv {
44 name = "statsd-runtime-deps";
45 pathsToLink = [ "/lib" ];
46 ignoreCollisions = true;
47
48 paths = backendsToPackages cfg.backends;
49 };
50
51in
52
53{
54
55 ###### interface
56
57 options.services.statsd = {
58
59 enable = mkEnableOption (lib.mdDoc "statsd");
60
61 listenAddress = mkOption {
62 description = lib.mdDoc "Address that statsd listens on over UDP";
63 default = "127.0.0.1";
64 type = types.str;
65 };
66
67 port = mkOption {
68 description = lib.mdDoc "Port that stats listens for messages on over UDP";
69 default = 8125;
70 type = types.int;
71 };
72
73 mgmt_address = mkOption {
74 description = lib.mdDoc "Address to run management TCP interface on";
75 default = "127.0.0.1";
76 type = types.str;
77 };
78
79 mgmt_port = mkOption {
80 description = lib.mdDoc "Port to run the management TCP interface on";
81 default = 8126;
82 type = types.int;
83 };
84
85 backends = mkOption {
86 description = lib.mdDoc "List of backends statsd will use for data persistence";
87 default = [];
88 example = [
89 "graphite"
90 "console"
91 "repeater"
92 "statsd-librato-backend"
93 "stackdriver-statsd-backend"
94 "statsd-influxdb-backend"
95 ];
96 type = types.listOf types.str;
97 };
98
99 graphiteHost = mkOption {
100 description = lib.mdDoc "Hostname or IP of Graphite server";
101 default = null;
102 type = types.nullOr types.str;
103 };
104
105 graphitePort = mkOption {
106 description = lib.mdDoc "Port of Graphite server (i.e. carbon-cache).";
107 default = null;
108 type = types.nullOr types.int;
109 };
110
111 extraConfig = mkOption {
112 description = lib.mdDoc "Extra configuration options for statsd";
113 default = "";
114 type = types.nullOr types.str;
115 };
116
117 };
118
119 ###### implementation
120
121 config = mkIf cfg.enable {
122
123 assertions = map (backend: {
124 assertion = !isBuiltinBackend backend -> hasAttrByPath [ backend ] pkgs.nodePackages;
125 message = "Only builtin backends (graphite, console, repeater) or backends enumerated in `pkgs.nodePackages` are allowed!";
126 }) cfg.backends;
127
128 users.users.statsd = {
129 uid = config.ids.uids.statsd;
130 description = "Statsd daemon user";
131 };
132
133 systemd.services.statsd = {
134 description = "Statsd Server";
135 wantedBy = [ "multi-user.target" ];
136 environment = {
137 NODE_PATH = "${deps}/lib/node_modules";
138 };
139 serviceConfig = {
140 ExecStart = "${pkgs.statsd}/bin/statsd ${configFile}";
141 User = "statsd";
142 };
143 };
144
145 environment.systemPackages = [ pkgs.statsd ];
146
147 };
148
149}