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 umask 077
121 mkdir -p ${configsDir}
122 ''
123 + (lib.optionalString (cfg.apiKeyFile != null) ''
124 cp --no-preserve=all "${cfg.apiKeyFile}" ${runDir}/longview.key
125 '')
126 + (lib.optionalString (cfg.apacheStatusUrl != "") ''
127 cat > ${configsDir}/Apache.conf <<EOF
128 location ${cfg.apacheStatusUrl}?auto
129 EOF
130 '')
131 + (lib.optionalString (cfg.mysqlUser != "" && cfg.mysqlPasswordFile != null) ''
132 cat > ${configsDir}/MySQL.conf <<EOF
133 username ${cfg.mysqlUser}
134 password `head -n1 "${cfg.mysqlPasswordFile}"`
135 EOF
136 '')
137 + (lib.optionalString (cfg.nginxStatusUrl != "") ''
138 cat > ${configsDir}/Nginx.conf <<EOF
139 location ${cfg.nginxStatusUrl}
140 EOF
141 '');
142 };
143
144 warnings =
145 let
146 warn =
147 k: lib.optional (cfg.${k} != "") "config.services.longview.${k} is insecure. Use ${k}File instead.";
148 in
149 lib.concatMap warn [
150 "apiKey"
151 "mysqlPassword"
152 ];
153
154 assertions = [
155 {
156 assertion = cfg.apiKeyFile != null;
157 message = "Longview needs an API key configured";
158 }
159 ];
160
161 # Create API key file if not configured.
162 services.longview.apiKeyFile = lib.mkIf (cfg.apiKey != "") (
163 lib.mkDefault (
164 toString (
165 pkgs.writeTextFile {
166 name = "longview.key";
167 text = cfg.apiKey;
168 }
169 )
170 )
171 );
172
173 # Create MySQL password file if not configured.
174 services.longview.mysqlPasswordFile = lib.mkDefault (
175 toString (
176 pkgs.writeTextFile {
177 name = "mysql-password-file";
178 text = cfg.mysqlPassword;
179 }
180 )
181 );
182 };
183}