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