1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.longview;
9
10 runDir = "/run/longview";
11 configsDir = "${runDir}/longview.d";
12
13in
14{
15 options = {
16
17 services.longview = {
18
19 enable = lib.mkOption {
20 type = lib.types.bool;
21 default = false;
22 description = ''
23 If enabled, system metrics will be sent to Linode LongView.
24 '';
25 };
26
27 apiKey = lib.mkOption {
28 type = lib.types.str;
29 default = "";
30 example = "01234567-89AB-CDEF-0123456789ABCDEF";
31 description = ''
32 Longview API key. To get this, look in Longview settings which
33 are found at <https://manager.linode.com/longview/>.
34
35 Warning: this secret is stored in the world-readable Nix store!
36 Use {option}`apiKeyFile` instead.
37 '';
38 };
39
40 apiKeyFile = lib.mkOption {
41 type = lib.types.nullOr lib.types.path;
42 default = null;
43 example = "/run/keys/longview-api-key";
44 description = ''
45 A file containing the Longview API key.
46 To get this, look in Longview settings which
47 are found at <https://manager.linode.com/longview/>.
48
49 {option}`apiKeyFile` takes precedence over {option}`apiKey`.
50 '';
51 };
52
53 apacheStatusUrl = lib.mkOption {
54 type = lib.types.str;
55 default = "";
56 example = "http://127.0.0.1/server-status";
57 description = ''
58 The Apache status page URL. If provided, Longview will
59 gather statistics from this location. This requires Apache
60 mod_status to be loaded and enabled.
61 '';
62 };
63
64 nginxStatusUrl = lib.mkOption {
65 type = lib.types.str;
66 default = "";
67 example = "http://127.0.0.1/nginx_status";
68 description = ''
69 The Nginx status page URL. Longview will gather statistics
70 from this URL. This requires the Nginx stub_status module to
71 be enabled and configured at the given location.
72 '';
73 };
74
75 mysqlUser = lib.mkOption {
76 type = lib.types.str;
77 default = "";
78 description = ''
79 The user for connecting to the MySQL database. If provided,
80 Longview will connect to MySQL and collect statistics about
81 queries, etc. This user does not need to have been granted
82 any extra privileges.
83 '';
84 };
85
86 mysqlPassword = lib.mkOption {
87 type = lib.types.str;
88 default = "";
89 description = ''
90 The password corresponding to {option}`mysqlUser`.
91 Warning: this is stored in cleartext in the Nix store!
92 Use {option}`mysqlPasswordFile` instead.
93 '';
94 };
95
96 mysqlPasswordFile = lib.mkOption {
97 type = lib.types.nullOr lib.types.path;
98 default = null;
99 example = "/run/keys/dbpassword";
100 description = ''
101 A file containing the password corresponding to {option}`mysqlUser`.
102 '';
103 };
104
105 };
106
107 };
108
109 config = lib.mkIf cfg.enable {
110 systemd.services.longview = {
111 description = "Longview Metrics Collection";
112 after = [ "network.target" ];
113 wantedBy = [ "multi-user.target" ];
114 serviceConfig.Type = "forking";
115 serviceConfig.ExecStop = "-${pkgs.coreutils}/bin/kill -TERM $MAINPID";
116 serviceConfig.ExecReload = "-${pkgs.coreutils}/bin/kill -HUP $MAINPID";
117 serviceConfig.PIDFile = "${runDir}/longview.pid";
118 serviceConfig.ExecStart = "${pkgs.longview}/bin/longview";
119 preStart =
120 ''
121 umask 077
122 mkdir -p ${configsDir}
123 ''
124 + (lib.optionalString (cfg.apiKeyFile != null) ''
125 cp --no-preserve=all "${cfg.apiKeyFile}" ${runDir}/longview.key
126 '')
127 + (lib.optionalString (cfg.apacheStatusUrl != "") ''
128 cat > ${configsDir}/Apache.conf <<EOF
129 location ${cfg.apacheStatusUrl}?auto
130 EOF
131 '')
132 + (lib.optionalString (cfg.mysqlUser != "" && cfg.mysqlPasswordFile != null) ''
133 cat > ${configsDir}/MySQL.conf <<EOF
134 username ${cfg.mysqlUser}
135 password `head -n1 "${cfg.mysqlPasswordFile}"`
136 EOF
137 '')
138 + (lib.optionalString (cfg.nginxStatusUrl != "") ''
139 cat > ${configsDir}/Nginx.conf <<EOF
140 location ${cfg.nginxStatusUrl}
141 EOF
142 '');
143 };
144
145 warnings =
146 let
147 warn =
148 k: lib.optional (cfg.${k} != "") "config.services.longview.${k} is insecure. Use ${k}File instead.";
149 in
150 lib.concatMap warn [
151 "apiKey"
152 "mysqlPassword"
153 ];
154
155 assertions = [
156 {
157 assertion = cfg.apiKeyFile != null;
158 message = "Longview needs an API key configured";
159 }
160 ];
161
162 # Create API key file if not configured.
163 services.longview.apiKeyFile = lib.mkIf (cfg.apiKey != "") (
164 lib.mkDefault (
165 toString (
166 pkgs.writeTextFile {
167 name = "longview.key";
168 text = cfg.apiKey;
169 }
170 )
171 )
172 );
173
174 # Create MySQL password file if not configured.
175 services.longview.mysqlPasswordFile = lib.mkDefault (
176 toString (
177 pkgs.writeTextFile {
178 name = "mysql-password-file";
179 text = cfg.mysqlPassword;
180 }
181 )
182 );
183 };
184}