Commit | Line | Data |
---|---|---|
105369a4 DT |
1 | ;;; GNU Guix --- Functional package management for GNU |
2 | ;;; Copyright © 2015 David Thompson <davet@gnu.org> | |
9b1cee97 | 3 | ;;; Copyright © 2015, 2016 Ludovic Courtès <ludo@gnu.org> |
8823ed4e | 4 | ;;; Copyright © 2016 Leo Famulari <leo@famulari.name> |
67cadaca | 5 | ;;; Copyright © 2017 Christopher Baines <mail@cbaines.net> |
5ee4cd69 | 6 | ;;; Copyright © 2018 Clément Lassieur <clement@lassieur.org> |
0d57a50a | 7 | ;;; Copyright © 2018 Julien Lepiller <julien@lepiller.eu> |
334a2f4d | 8 | ;;; Copyright © 2019 Robert Vollmert <rob@vllmrt.net> |
e20388ad | 9 | ;;; Copyright © 2020 Marius Bakke <marius@gnu.org> |
105369a4 DT |
10 | ;;; |
11 | ;;; This file is part of GNU Guix. | |
12 | ;;; | |
13 | ;;; GNU Guix is free software; you can redistribute it and/or modify it | |
14 | ;;; under the terms of the GNU General Public License as published by | |
15 | ;;; the Free Software Foundation; either version 3 of the License, or (at | |
16 | ;;; your option) any later version. | |
17 | ;;; | |
18 | ;;; GNU Guix is distributed in the hope that it will be useful, but | |
19 | ;;; WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | ;;; GNU General Public License for more details. | |
22 | ;;; | |
23 | ;;; You should have received a copy of the GNU General Public License | |
24 | ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. | |
25 | ||
26 | (define-module (gnu services databases) | |
27 | #:use-module (gnu services) | |
0190c1c0 | 28 | #:use-module (gnu services shepherd) |
105369a4 DT |
29 | #:use-module (gnu system shadow) |
30 | #:use-module (gnu packages admin) | |
31 | #:use-module (gnu packages databases) | |
0d57a50a JL |
32 | #:use-module (guix build-system trivial) |
33 | #:use-module (guix build union) | |
a698df72 | 34 | #:use-module (guix deprecation) |
119fdd0d | 35 | #:use-module (guix modules) |
0d57a50a | 36 | #:use-module (guix packages) |
105369a4 | 37 | #:use-module (guix records) |
105369a4 | 38 | #:use-module (guix gexp) |
936e7a52 | 39 | #:use-module (srfi srfi-1) |
0adfe95a | 40 | #:use-module (ice-9 match) |
383c51ca | 41 | #:export (postgresql-config-file |
936e7a52 CB |
42 | postgresql-config-file? |
43 | postgresql-config-file-log-destination | |
44 | postgresql-config-file-hba-file | |
45 | postgresql-config-file-ident-file | |
6c067921 | 46 | postgresql-config-file-socket-directory |
936e7a52 CB |
47 | postgresql-config-file-extra-config |
48 | ||
488ea71e | 49 | postgresql-configuration |
24e96431 | 50 | postgresql-configuration? |
488ea71e CB |
51 | postgresql-configuration-postgresql |
52 | postgresql-configuration-port | |
53 | postgresql-configuration-locale | |
54 | postgresql-configuration-file | |
fe4b8823 | 55 | postgresql-configuration-log-directory |
488ea71e CB |
56 | postgresql-configuration-data-directory |
57 | ||
24e96431 TČ |
58 | postgresql-service |
59 | postgresql-service-type | |
60 | ||
ec145a2f MO |
61 | postgresql-role |
62 | postgresql-role? | |
63 | postgresql-role-name | |
64 | postgresql-role-permissions | |
65 | postgresql-role-create-database? | |
66 | postgresql-role-configuration | |
67 | postgresql-role-configuration? | |
68 | postgresql-role-configuration-host | |
69 | postgresql-role-configuration-roles | |
70 | ||
71 | postgresql-role-service-type | |
72 | ||
119fdd0d | 73 | memcached-service-type |
119fdd0d CB |
74 | memcached-configuration |
75 | memcached-configuration? | |
76 | memcached-configuration-memecached | |
77 | memcached-configuration-interfaces | |
78 | memcached-configuration-tcp-port | |
79 | memcached-configuration-udp-port | |
80 | memcached-configuration-additional-options | |
81 | ||
5266ff71 CB |
82 | mongodb-configuration |
83 | mongodb-configuration? | |
84 | mongodb-configuration-mongodb | |
85 | mongodb-configuration-config-file | |
86 | mongodb-configuration-data-directory | |
87 | mongodb-service-type | |
88 | ||
6575183b | 89 | mysql-service |
24e96431 TČ |
90 | mysql-service-type |
91 | mysql-configuration | |
67cadaca CB |
92 | mysql-configuration? |
93 | ||
94 | redis-configuration | |
95 | redis-configuration? | |
96 | redis-service-type)) | |
105369a4 DT |
97 | |
98 | ;;; Commentary: | |
99 | ;;; | |
100 | ;;; Database services. | |
101 | ;;; | |
102 | ;;; Code: | |
103 | ||
936e7a52 CB |
104 | (define %default-postgres-hba |
105 | (plain-file "pg_hba.conf" | |
106 | " | |
334a2f4d RV |
107 | local all all peer |
108 | host all all 127.0.0.1/32 md5 | |
109 | host all all ::1/128 md5")) | |
936e7a52 CB |
110 | |
111 | (define %default-postgres-ident | |
112 | (plain-file "pg_ident.conf" | |
113 | "# MAPNAME SYSTEM-USERNAME PG-USERNAME")) | |
114 | ||
115 | (define-record-type* <postgresql-config-file> | |
116 | postgresql-config-file make-postgresql-config-file | |
117 | postgresql-config-file? | |
6c067921 MO |
118 | (log-destination postgresql-config-file-log-destination |
119 | (default "syslog")) | |
120 | (hba-file postgresql-config-file-hba-file | |
121 | (default %default-postgres-hba)) | |
122 | (ident-file postgresql-config-file-ident-file | |
123 | (default %default-postgres-ident)) | |
124 | (socket-directory postgresql-config-file-socket-directory | |
125 | (default "/var/run/postgresql")) | |
126 | (extra-config postgresql-config-file-extra-config | |
127 | (default '()))) | |
936e7a52 CB |
128 | |
129 | (define-gexp-compiler (postgresql-config-file-compiler | |
130 | (file <postgresql-config-file>) system target) | |
131 | (match file | |
132 | (($ <postgresql-config-file> log-destination hba-file | |
6c067921 MO |
133 | ident-file socket-directory |
134 | extra-config) | |
a38d0b01 MO |
135 | ;; See: https://www.postgresql.org/docs/current/config-setting.html. |
136 | (define (format-value value) | |
137 | (cond | |
138 | ((boolean? value) | |
139 | (list (if value "on" "off"))) | |
140 | ((number? value) | |
141 | (list (number->string value))) | |
142 | (else | |
143 | (list "'" value "'")))) | |
144 | ||
145 | (define contents | |
146 | (append-map | |
147 | (match-lambda | |
148 | ((key) '()) | |
149 | ((key . #f) '()) | |
150 | ((key values ...) | |
151 | `(,key " = " ,@(append-map format-value values) "\n"))) | |
152 | ||
153 | `(("log_destination" ,log-destination) | |
154 | ("hba_file" ,hba-file) | |
155 | ("ident_file" ,ident-file) | |
6c067921 MO |
156 | ,@(if socket-directory |
157 | `(("unix_socket_directories" ,socket-directory)) | |
158 | '()) | |
a38d0b01 | 159 | ,@extra-config))) |
936e7a52 CB |
160 | |
161 | (gexp->derivation | |
162 | "postgresql.conf" | |
163 | #~(call-with-output-file (ungexp output "out") | |
164 | (lambda (port) | |
165 | (display | |
166 | (string-append #$@contents) | |
167 | port))) | |
168 | #:local-build? #t)))) | |
169 | ||
0adfe95a LC |
170 | (define-record-type* <postgresql-configuration> |
171 | postgresql-configuration make-postgresql-configuration | |
172 | postgresql-configuration? | |
bdcf4d88 | 173 | (postgresql postgresql-configuration-postgresql) ;<package> |
0d57a50a JL |
174 | (port postgresql-configuration-port |
175 | (default 5432)) | |
176 | (locale postgresql-configuration-locale | |
177 | (default "en_US.utf8")) | |
178 | (config-file postgresql-configuration-file | |
179 | (default (postgresql-config-file))) | |
fe4b8823 MO |
180 | (log-directory postgresql-configuration-log-directory |
181 | (default "/var/log/postgresql")) | |
0d57a50a JL |
182 | (data-directory postgresql-configuration-data-directory |
183 | (default "/var/lib/postgresql/data")) | |
184 | (extension-packages postgresql-configuration-extension-packages | |
185 | (default '()))) | |
0adfe95a | 186 | |
0adfe95a LC |
187 | (define %postgresql-accounts |
188 | (list (user-group (name "postgres") (system? #t)) | |
189 | (user-account | |
190 | (name "postgres") | |
191 | (group "postgres") | |
192 | (system? #t) | |
193 | (comment "PostgreSQL server user") | |
194 | (home-directory "/var/empty") | |
9e41130b | 195 | (shell (file-append shadow "/sbin/nologin"))))) |
0adfe95a | 196 | |
0d57a50a JL |
197 | (define (final-postgresql postgresql extension-packages) |
198 | (if (null? extension-packages) | |
199 | postgresql | |
200 | (package | |
201 | (inherit postgresql) | |
202 | (source #f) | |
203 | (build-system trivial-build-system) | |
204 | (arguments | |
205 | `(#:modules ((guix build utils) (guix build union)) | |
206 | #:builder | |
207 | (begin | |
208 | (use-modules (guix build utils) (guix build union) (srfi srfi-26)) | |
33687aa3 MO |
209 | (union-build (assoc-ref %outputs "out") |
210 | (map (lambda (input) (cdr input)) | |
211 | %build-inputs)) | |
0d57a50a JL |
212 | #t))) |
213 | (inputs | |
214 | `(("postgresql" ,postgresql) | |
215 | ,@(map (lambda (extension) (list "extension" extension)) | |
216 | extension-packages)))))) | |
217 | ||
0adfe95a LC |
218 | (define postgresql-activation |
219 | (match-lambda | |
fe4b8823 MO |
220 | (($ <postgresql-configuration> postgresql port locale config-file |
221 | log-directory data-directory | |
222 | extension-packages) | |
0adfe95a LC |
223 | #~(begin |
224 | (use-modules (guix build utils) | |
225 | (ice-9 match)) | |
226 | ||
227 | (let ((user (getpwnam "postgres")) | |
fe4b8823 MO |
228 | (initdb (string-append |
229 | #$(final-postgresql postgresql | |
230 | extension-packages) | |
231 | "/bin/initdb")) | |
e05b780a CB |
232 | (initdb-args |
233 | (append | |
234 | (if #$locale | |
235 | (list (string-append "--locale=" #$locale)) | |
236 | '())))) | |
0adfe95a LC |
237 | ;; Create db state directory. |
238 | (mkdir-p #$data-directory) | |
239 | (chown #$data-directory (passwd:uid user) (passwd:gid user)) | |
240 | ||
6c067921 MO |
241 | ;; Create the socket directory. |
242 | (let ((socket-directory | |
243 | #$(postgresql-config-file-socket-directory config-file))) | |
244 | (when (string? socket-directory) | |
245 | (mkdir-p socket-directory) | |
246 | (chown socket-directory (passwd:uid user) (passwd:gid user)))) | |
247 | ||
fe4b8823 MO |
248 | ;; Create the log directory. |
249 | (when (string? #$log-directory) | |
250 | (mkdir-p #$log-directory) | |
251 | (chown #$log-directory (passwd:uid user) (passwd:gid user))) | |
252 | ||
0adfe95a LC |
253 | ;; Drop privileges and init state directory in a new |
254 | ;; process. Wait for it to finish before proceeding. | |
255 | (match (primitive-fork) | |
256 | (0 | |
257 | ;; Exit with a non-zero status code if an exception is thrown. | |
258 | (dynamic-wind | |
259 | (const #t) | |
260 | (lambda () | |
261 | (setgid (passwd:gid user)) | |
262 | (setuid (passwd:uid user)) | |
e05b780a CB |
263 | (primitive-exit |
264 | (apply system* | |
265 | initdb | |
266 | "-D" | |
267 | #$data-directory | |
268 | initdb-args))) | |
0adfe95a LC |
269 | (lambda () |
270 | (primitive-exit 1)))) | |
271 | (pid (waitpid pid)))))))) | |
272 | ||
d4053c71 | 273 | (define postgresql-shepherd-service |
0adfe95a | 274 | (match-lambda |
fe4b8823 MO |
275 | (($ <postgresql-configuration> postgresql port locale config-file |
276 | log-directory data-directory | |
277 | extension-packages) | |
5ee4cd69 CL |
278 | (let* ((pg_ctl-wrapper |
279 | ;; Wrapper script that switches to the 'postgres' user before | |
280 | ;; launching daemon. | |
281 | (program-file | |
282 | "pg_ctl-wrapper" | |
283 | #~(begin | |
284 | (use-modules (ice-9 match) | |
285 | (ice-9 format)) | |
286 | (match (command-line) | |
287 | ((_ mode) | |
288 | (let ((user (getpwnam "postgres")) | |
fe4b8823 MO |
289 | (pg_ctl #$(file-append |
290 | (final-postgresql postgresql | |
291 | extension-packages) | |
0d57a50a | 292 | "/bin/pg_ctl")) |
5ee4cd69 CL |
293 | (options (format #f "--config-file=~a -p ~d" |
294 | #$config-file #$port))) | |
295 | (setgid (passwd:gid user)) | |
296 | (setuid (passwd:uid user)) | |
fe4b8823 MO |
297 | (execl pg_ctl pg_ctl "-D" #$data-directory |
298 | #$@(if (string? log-directory) | |
299 | (list "-l" | |
300 | (string-append log-directory | |
301 | "/pg_ctl.log")) | |
302 | '()) | |
303 | "-o" options | |
5ee4cd69 | 304 | mode))))))) |
ef2dda8e | 305 | (pid-file (in-vicinity data-directory "postmaster.pid")) |
5ee4cd69 CL |
306 | (action (lambda args |
307 | #~(lambda _ | |
ef2dda8e CL |
308 | (invoke #$pg_ctl-wrapper #$@args) |
309 | (match '#$args | |
310 | (("start") | |
311 | (call-with-input-file #$pid-file read)) | |
312 | (_ #t)))))) | |
d4053c71 | 313 | (list (shepherd-service |
0adfe95a LC |
314 | (provision '(postgres)) |
315 | (documentation "Run the PostgreSQL daemon.") | |
9b1cee97 | 316 | (requirement '(user-processes loopback syslogd)) |
ef2dda8e CL |
317 | (modules `((ice-9 match) |
318 | ,@%default-modules)) | |
5ee4cd69 CL |
319 | (start (action "start")) |
320 | (stop (action "stop")))))))) | |
0adfe95a LC |
321 | |
322 | (define postgresql-service-type | |
33687aa3 MO |
323 | (service-type |
324 | (name 'postgresql) | |
325 | (extensions | |
326 | (list (service-extension shepherd-root-service-type | |
327 | postgresql-shepherd-service) | |
328 | (service-extension activation-service-type | |
329 | postgresql-activation) | |
330 | (service-extension account-service-type | |
331 | (const %postgresql-accounts)) | |
332 | (service-extension | |
333 | profile-service-type | |
8163f745 MO |
334 | (compose list postgresql-configuration-postgresql)))) |
335 | (default-value (postgresql-configuration | |
336 | (postgresql postgresql-10))))) | |
0adfe95a | 337 | |
a698df72 CB |
338 | (define-deprecated (postgresql-service #:key (postgresql postgresql) |
339 | (port 5432) | |
340 | (locale "en_US.utf8") | |
341 | (config-file (postgresql-config-file)) | |
33687aa3 MO |
342 | (data-directory |
343 | "/var/lib/postgresql/data") | |
a698df72 CB |
344 | (extension-packages '())) |
345 | postgresql-service-type | |
33687aa3 MO |
346 | "Return a service that runs @var{postgresql}, the PostgreSQL database |
347 | server. | |
105369a4 DT |
348 | |
349 | The PostgreSQL daemon loads its runtime configuration from @var{config-file} | |
350 | and stores the database cluster in @var{data-directory}." | |
0adfe95a LC |
351 | (service postgresql-service-type |
352 | (postgresql-configuration | |
353 | (postgresql postgresql) | |
2d3d5cc5 | 354 | (port port) |
e05b780a | 355 | (locale locale) |
0adfe95a | 356 | (config-file config-file) |
0d57a50a JL |
357 | (data-directory data-directory) |
358 | (extension-packages extension-packages)))) | |
6575183b | 359 | |
ec145a2f MO |
360 | (define-record-type* <postgresql-role> |
361 | postgresql-role make-postgresql-role | |
362 | postgresql-role? | |
363 | (name postgresql-role-name) ;string | |
364 | (permissions postgresql-role-permissions | |
365 | (default '(createdb login))) ;list | |
366 | (create-database? postgresql-role-create-database? ;boolean | |
367 | (default #f))) | |
368 | ||
369 | (define-record-type* <postgresql-role-configuration> | |
370 | postgresql-role-configuration make-postgresql-role-configuration | |
371 | postgresql-role-configuration? | |
372 | (host postgresql-role-configuration-host ;string | |
373 | (default "/var/run/postgresql")) | |
374 | (log postgresql-role-configuration-log ;string | |
375 | (default "/var/log/postgresql_roles.log")) | |
376 | (roles postgresql-role-configuration-roles | |
377 | (default '()))) ;list | |
378 | ||
379 | (define (postgresql-create-roles config) | |
380 | ;; See: https://www.postgresql.org/docs/current/sql-createrole.html for the | |
381 | ;; complete permissions list. | |
382 | (define (format-permissions permissions) | |
383 | (let ((dict '(bypassrls createdb createrole login replication superuser))) | |
384 | (string-join (filter-map (lambda (permission) | |
385 | (and (member permission dict) | |
386 | (string-upcase | |
387 | (symbol->string permission)))) | |
388 | permissions) | |
389 | " "))) | |
390 | ||
391 | (define (roles->queries roles) | |
392 | (apply mixed-text-file "queries" | |
393 | (append-map | |
394 | (lambda (role) | |
395 | (match-record role <postgresql-role> | |
396 | (name permissions create-database?) | |
397 | `("SELECT NOT(EXISTS(SELECT 1 FROM pg_catalog.pg_roles WHERE \ | |
398 | rolname = '" ,name "')) as not_exists;\n" | |
399 | "\\gset\n" | |
400 | "\\if :not_exists\n" | |
221985ce | 401 | "CREATE ROLE \"" ,name "\"" |
ec145a2f MO |
402 | " WITH " ,(format-permissions permissions) |
403 | ";\n" | |
404 | ,@(if create-database? | |
221985ce MB |
405 | `("CREATE DATABASE \"" ,name "\"" |
406 | " OWNER \"" ,name "\";\n") | |
ec145a2f MO |
407 | '()) |
408 | "\\endif\n"))) | |
409 | roles))) | |
410 | ||
411 | (let ((host (postgresql-role-configuration-host config)) | |
412 | (roles (postgresql-role-configuration-roles config))) | |
413 | (program-file | |
414 | "postgresql-create-roles" | |
415 | #~(begin | |
416 | (let ((psql #$(file-append postgresql "/bin/psql"))) | |
417 | (execl psql psql "-a" | |
418 | "-h" #$host | |
419 | "-f" #$(roles->queries roles))))))) | |
420 | ||
421 | (define (postgresql-role-shepherd-service config) | |
422 | (match-record config <postgresql-role-configuration> | |
423 | (log) | |
424 | (list (shepherd-service | |
425 | (requirement '(postgres)) | |
426 | (provision '(postgres-roles)) | |
427 | (one-shot? #t) | |
428 | (start #~(make-forkexec-constructor | |
429 | (list #$(postgresql-create-roles config)) | |
430 | #:user "postgres" #:group "postgres" | |
431 | #:log-file #$log)) | |
432 | (documentation "Create PostgreSQL roles."))))) | |
433 | ||
434 | (define postgresql-role-service-type | |
435 | (service-type (name 'postgresql-role) | |
436 | (extensions | |
437 | (list (service-extension shepherd-root-service-type | |
438 | postgresql-role-shepherd-service))) | |
439 | (compose concatenate) | |
440 | (extend (lambda (config extended-roles) | |
441 | (match-record config <postgresql-role-configuration> | |
442 | (host roles) | |
443 | (postgresql-role-configuration | |
444 | (host host) | |
445 | (roles (append roles extended-roles)))))) | |
446 | (default-value (postgresql-role-configuration)) | |
447 | (description "Ensure the specified PostgreSQL roles are | |
448 | created after the PostgreSQL database is started."))) | |
449 | ||
6575183b | 450 | \f |
119fdd0d CB |
451 | ;;; |
452 | ;;; Memcached | |
453 | ;;; | |
454 | ||
455 | (define-record-type* <memcached-configuration> | |
456 | memcached-configuration make-memcached-configuration | |
457 | memcached-configuration? | |
458 | (memcached memcached-configuration-memcached ;<package> | |
459 | (default memcached)) | |
460 | (interfaces memcached-configuration-interfaces | |
461 | (default '("0.0.0.0"))) | |
462 | (tcp-port memcached-configuration-tcp-port | |
463 | (default 11211)) | |
464 | (udp-port memcached-configuration-udp-port | |
465 | (default 11211)) | |
466 | (additional-options memcached-configuration-additional-options | |
467 | (default '()))) | |
468 | ||
469 | (define %memcached-accounts | |
470 | (list (user-group (name "memcached") (system? #t)) | |
471 | (user-account | |
472 | (name "memcached") | |
473 | (group "memcached") | |
474 | (system? #t) | |
475 | (comment "Memcached server user") | |
476 | (home-directory "/var/empty") | |
477 | (shell (file-append shadow "/sbin/nologin"))))) | |
478 | ||
6230e155 CB |
479 | (define memcached-activation |
480 | #~(begin | |
481 | (use-modules (guix build utils)) | |
482 | (let ((user (getpwnam "memcached"))) | |
483 | (mkdir-p "/var/run/memcached") | |
484 | (chown "/var/run/memcached" | |
485 | (passwd:uid user) (passwd:gid user))))) | |
486 | ||
119fdd0d CB |
487 | (define memcached-shepherd-service |
488 | (match-lambda | |
489 | (($ <memcached-configuration> memcached interfaces tcp-port udp-port | |
490 | additional-options) | |
491 | (with-imported-modules (source-module-closure | |
492 | '((gnu build shepherd))) | |
493 | (list (shepherd-service | |
494 | (provision '(memcached)) | |
495 | (documentation "Run the Memcached daemon.") | |
496 | (requirement '(user-processes loopback)) | |
497 | (modules '((gnu build shepherd))) | |
498 | (start #~(make-forkexec-constructor | |
499 | `(#$(file-append memcached "/bin/memcached") | |
500 | "-l" #$(string-join interfaces ",") | |
501 | "-p" #$(number->string tcp-port) | |
502 | "-U" #$(number->string udp-port) | |
503 | "--daemon" | |
6230e155 CB |
504 | ;; Memcached changes to the memcached user prior to |
505 | ;; writing the pid file, so write it to a directory | |
506 | ;; that memcached owns. | |
507 | "-P" "/var/run/memcached/pid" | |
119fdd0d CB |
508 | "-u" "memcached" |
509 | ,#$@additional-options) | |
510 | #:log-file "/var/log/memcached" | |
6230e155 | 511 | #:pid-file "/var/run/memcached/pid")) |
119fdd0d CB |
512 | (stop #~(make-kill-destructor)))))))) |
513 | ||
514 | (define memcached-service-type | |
515 | (service-type (name 'memcached) | |
516 | (extensions | |
517 | (list (service-extension shepherd-root-service-type | |
518 | memcached-shepherd-service) | |
6230e155 CB |
519 | (service-extension activation-service-type |
520 | (const memcached-activation)) | |
119fdd0d CB |
521 | (service-extension account-service-type |
522 | (const %memcached-accounts)))) | |
523 | (default-value (memcached-configuration)))) | |
524 | ||
525 | \f | |
5266ff71 CB |
526 | ;;; |
527 | ;;; MongoDB | |
528 | ;;; | |
529 | ||
530 | (define %default-mongodb-configuration-file | |
531 | (plain-file | |
532 | "mongodb.yaml" | |
533 | "# GNU Guix: MongoDB default configuration file | |
534 | processManagement: | |
535 | pidFilePath: /var/run/mongodb/pid | |
536 | storage: | |
537 | dbPath: /var/lib/mongodb | |
538 | ")) | |
539 | ||
540 | ||
541 | (define-record-type* <mongodb-configuration> | |
542 | mongodb-configuration make-mongodb-configuration | |
543 | mongodb-configuration? | |
544 | (mongodb mongodb-configuration-mongodb | |
545 | (default mongodb)) | |
546 | (config-file mongodb-configuration-config-file | |
547 | (default %default-mongodb-configuration-file)) | |
548 | (data-directory mongodb-configuration-data-directory | |
549 | (default "/var/lib/mongodb"))) | |
550 | ||
551 | (define %mongodb-accounts | |
552 | (list (user-group (name "mongodb") (system? #t)) | |
553 | (user-account | |
554 | (name "mongodb") | |
555 | (group "mongodb") | |
556 | (system? #t) | |
557 | (comment "Mongodb server user") | |
558 | (home-directory "/var/lib/mongodb") | |
559 | (shell (file-append shadow "/sbin/nologin"))))) | |
560 | ||
561 | (define mongodb-activation | |
562 | (match-lambda | |
563 | (($ <mongodb-configuration> mongodb config-file data-directory) | |
564 | #~(begin | |
565 | (use-modules (guix build utils)) | |
566 | (let ((user (getpwnam "mongodb"))) | |
567 | (for-each | |
568 | (lambda (directory) | |
569 | (mkdir-p directory) | |
570 | (chown directory | |
571 | (passwd:uid user) (passwd:gid user))) | |
572 | '("/var/run/mongodb" #$data-directory))))))) | |
573 | ||
574 | (define mongodb-shepherd-service | |
575 | (match-lambda | |
576 | (($ <mongodb-configuration> mongodb config-file data-directory) | |
577 | (shepherd-service | |
578 | (provision '(mongodb)) | |
579 | (documentation "Run the Mongodb daemon.") | |
580 | (requirement '(user-processes loopback)) | |
581 | (start #~(make-forkexec-constructor | |
582 | `(,(string-append #$mongodb "/bin/mongod") | |
583 | "--config" | |
584 | ,#$config-file) | |
585 | #:user "mongodb" | |
586 | #:group "mongodb" | |
587 | #:pid-file "/var/run/mongodb/pid" | |
588 | #:log-file "/var/log/mongodb.log")) | |
589 | (stop #~(make-kill-destructor)))))) | |
590 | ||
591 | (define mongodb-service-type | |
592 | (service-type | |
593 | (name 'mongodb) | |
594 | (description "Run the MongoDB document database server.") | |
595 | (extensions | |
596 | (list (service-extension shepherd-root-service-type | |
597 | (compose list | |
598 | mongodb-shepherd-service)) | |
599 | (service-extension activation-service-type | |
600 | mongodb-activation) | |
601 | (service-extension account-service-type | |
602 | (const %mongodb-accounts)))) | |
603 | (default-value | |
604 | (mongodb-configuration)))) | |
605 | ||
606 | \f | |
6575183b SB |
607 | ;;; |
608 | ;;; MySQL. | |
609 | ;;; | |
610 | ||
611 | (define-record-type* <mysql-configuration> | |
612 | mysql-configuration make-mysql-configuration | |
613 | mysql-configuration? | |
4b41febf | 614 | (mysql mysql-configuration-mysql (default mariadb)) |
27d7cdbf | 615 | (bind-address mysql-configuration-bind-address (default "127.0.0.1")) |
33ed8296 | 616 | (port mysql-configuration-port (default 3306)) |
927bf98e | 617 | (socket mysql-configuration-socket (default "/run/mysqld/mysqld.sock")) |
e20388ad MB |
618 | (extra-content mysql-configuration-extra-content (default "")) |
619 | (auto-upgrade? mysql-configuration-auto-upgrade? (default #t))) | |
6575183b SB |
620 | |
621 | (define %mysql-accounts | |
622 | (list (user-group | |
623 | (name "mysql") | |
624 | (system? #t)) | |
625 | (user-account | |
626 | (name "mysql") | |
627 | (group "mysql") | |
628 | (system? #t) | |
629 | (home-directory "/var/empty") | |
9e41130b | 630 | (shell (file-append shadow "/sbin/nologin"))))) |
6575183b SB |
631 | |
632 | (define mysql-configuration-file | |
633 | (match-lambda | |
927bf98e | 634 | (($ <mysql-configuration> mysql bind-address port socket extra-content) |
4b41febf | 635 | (mixed-text-file "my.cnf" "[mysqld] |
6575183b | 636 | datadir=/var/lib/mysql |
927bf98e | 637 | socket=" socket " |
27d7cdbf | 638 | bind-address=" bind-address " |
4b41febf | 639 | port=" (number->string port) " |
33ed8296 | 640 | " extra-content " |
6575183b SB |
641 | ")))) |
642 | ||
643 | (define (%mysql-activation config) | |
644 | "Return an activation gexp for the MySQL or MariaDB database server." | |
645 | (let ((mysql (mysql-configuration-mysql config)) | |
646 | (my.cnf (mysql-configuration-file config))) | |
647 | #~(begin | |
648 | (use-modules (ice-9 popen) | |
649 | (guix build utils)) | |
650 | (let* ((mysqld (string-append #$mysql "/bin/mysqld")) | |
651 | (user (getpwnam "mysql")) | |
652 | (uid (passwd:uid user)) | |
653 | (gid (passwd:gid user)) | |
654 | (datadir "/var/lib/mysql") | |
655 | (rundir "/run/mysqld")) | |
656 | (mkdir-p datadir) | |
657 | (chown datadir uid gid) | |
658 | (mkdir-p rundir) | |
659 | (chown rundir uid gid) | |
660 | ;; Initialize the database when it doesn't exist. | |
661 | (when (not (file-exists? (string-append datadir "/mysql"))) | |
662 | (if (string-prefix? "mysql-" (strip-store-file-name #$mysql)) | |
663 | ;; For MySQL. | |
664 | (system* mysqld | |
665 | (string-append "--defaults-file=" #$my.cnf) | |
666 | "--initialize" | |
667 | "--user=mysql") | |
668 | ;; For MariaDB. | |
669 | ;; XXX: The 'mysql_install_db' script doesn't work directly | |
670 | ;; due to missing 'mkdir' in PATH. | |
671 | (let ((p (open-pipe* OPEN_WRITE mysqld | |
672 | (string-append | |
673 | "--defaults-file=" #$my.cnf) | |
674 | "--bootstrap" | |
675 | "--user=mysql"))) | |
676 | ;; Create the system database, as does by 'mysql_install_db'. | |
677 | (display "create database mysql;\n" p) | |
678 | (display "use mysql;\n" p) | |
679 | (for-each | |
680 | (lambda (sql) | |
681 | (call-with-input-file | |
5c3d77c3 | 682 | (string-append #$mysql:lib "/share/mysql/" sql) |
6575183b SB |
683 | (lambda (in) (dump-port in p)))) |
684 | '("mysql_system_tables.sql" | |
685 | "mysql_performance_tables.sql" | |
686 | "mysql_system_tables_data.sql" | |
687 | "fill_help_tables.sql")) | |
688 | ;; Remove the anonymous user and disable root access from | |
689 | ;; remote machines, as does by 'mysql_secure_installation'. | |
690 | (display " | |
691 | DELETE FROM user WHERE User=''; | |
692 | DELETE FROM user WHERE User='root' AND | |
693 | Host NOT IN ('localhost', '127.0.0.1', '::1'); | |
694 | FLUSH PRIVILEGES; | |
695 | " p) | |
696 | (close-pipe p)))))))) | |
697 | ||
698 | (define (mysql-shepherd-service config) | |
699 | (list (shepherd-service | |
700 | (provision '(mysql)) | |
701 | (documentation "Run the MySQL server.") | |
702 | (start (let ((mysql (mysql-configuration-mysql config)) | |
703 | (my.cnf (mysql-configuration-file config))) | |
704 | #~(make-forkexec-constructor | |
705 | (list (string-append #$mysql "/bin/mysqld") | |
706 | (string-append "--defaults-file=" #$my.cnf)) | |
707 | #:user "mysql" #:group "mysql"))) | |
708 | (stop #~(make-kill-destructor))))) | |
709 | ||
e20388ad MB |
710 | (define (mysql-upgrade-wrapper mysql socket-file) |
711 | ;; The MySQL socket and PID file may appear before the server is ready to | |
712 | ;; accept connections. Ensure the socket is responsive before attempting | |
713 | ;; to run the upgrade script. | |
714 | (program-file | |
715 | "mysql-upgrade-wrapper" | |
716 | #~(begin | |
717 | (let ((mysql-upgrade #$(file-append mysql "/bin/mysql_upgrade")) | |
718 | (timeout 10)) | |
719 | (begin | |
720 | (let loop ((i 0)) | |
721 | (catch 'system-error | |
722 | (lambda () | |
723 | (let ((sock (socket PF_UNIX SOCK_STREAM 0))) | |
724 | (connect sock AF_UNIX #$socket-file) | |
725 | (close-port sock) | |
726 | ;; The socket is ready! | |
727 | (execl mysql-upgrade mysql-upgrade | |
728 | (string-append "--socket=" #$socket-file)))) | |
3cf19b83 MB |
729 | (lambda args |
730 | (if (< i timeout) | |
731 | (begin | |
732 | (sleep 1) | |
733 | (loop (+ 1 i))) | |
734 | ;; No luck, give up. | |
735 | (throw 'timeout-error | |
736 | "MySQL server did not appear in time!")))))))))) | |
e20388ad MB |
737 | |
738 | (define (mysql-upgrade-shepherd-service config) | |
739 | (list (shepherd-service | |
740 | (provision '(mysql-upgrade)) | |
741 | (requirement '(mysql)) | |
742 | (one-shot? #t) | |
743 | (documentation "Upgrade MySQL database schemas.") | |
744 | (start (let ((mysql (mysql-configuration-mysql config)) | |
745 | (socket (mysql-configuration-socket config))) | |
746 | #~(make-forkexec-constructor | |
747 | (list #$(mysql-upgrade-wrapper mysql socket)) | |
748 | #:user "mysql" #:group "mysql")))))) | |
749 | ||
750 | (define (mysql-shepherd-services config) | |
751 | (if (mysql-configuration-auto-upgrade? config) | |
752 | (append (mysql-shepherd-service config) | |
753 | (mysql-upgrade-shepherd-service config)) | |
754 | (mysql-shepherd-service config))) | |
755 | ||
6575183b SB |
756 | (define mysql-service-type |
757 | (service-type | |
758 | (name 'mysql) | |
759 | (extensions | |
760 | (list (service-extension account-service-type | |
761 | (const %mysql-accounts)) | |
762 | (service-extension activation-service-type | |
763 | %mysql-activation) | |
764 | (service-extension shepherd-root-service-type | |
e20388ad | 765 | mysql-shepherd-services))) |
e903738f | 766 | (default-value (mysql-configuration)))) |
6575183b | 767 | |
89b704a4 MB |
768 | (define-deprecated (mysql-service #:key (config (mysql-configuration))) |
769 | mysql-service-type | |
6575183b | 770 | (service mysql-service-type config)) |
67cadaca CB |
771 | |
772 | \f | |
773 | ;;; | |
774 | ;;; Redis | |
775 | ;;; | |
776 | ||
777 | (define-record-type* <redis-configuration> | |
778 | redis-configuration make-redis-configuration | |
779 | redis-configuration? | |
780 | (redis redis-configuration-redis ;<package> | |
781 | (default redis)) | |
782 | (bind redis-configuration-bind | |
783 | (default "127.0.0.1")) | |
784 | (port redis-configuration-port | |
785 | (default 6379)) | |
786 | (working-directory redis-configuration-working-directory | |
787 | (default "/var/lib/redis")) | |
788 | (config-file redis-configuration-config-file | |
789 | (default #f))) | |
790 | ||
791 | (define (default-redis.conf bind port working-directory) | |
792 | (mixed-text-file "redis.conf" | |
793 | "bind " bind "\n" | |
794 | "port " (number->string port) "\n" | |
795 | "dir " working-directory "\n" | |
796 | "daemonize no\n")) | |
797 | ||
798 | (define %redis-accounts | |
799 | (list (user-group (name "redis") (system? #t)) | |
800 | (user-account | |
801 | (name "redis") | |
802 | (group "redis") | |
803 | (system? #t) | |
804 | (comment "Redis server user") | |
805 | (home-directory "/var/empty") | |
806 | (shell (file-append shadow "/sbin/nologin"))))) | |
807 | ||
808 | (define redis-activation | |
809 | (match-lambda | |
810 | (($ <redis-configuration> redis bind port working-directory config-file) | |
811 | #~(begin | |
812 | (use-modules (guix build utils) | |
813 | (ice-9 match)) | |
814 | ||
815 | (let ((user (getpwnam "redis"))) | |
816 | (mkdir-p #$working-directory) | |
817 | (chown #$working-directory (passwd:uid user) (passwd:gid user))))))) | |
818 | ||
819 | (define redis-shepherd-service | |
820 | (match-lambda | |
821 | (($ <redis-configuration> redis bind port working-directory config-file) | |
822 | (let ((config-file | |
823 | (or config-file | |
824 | (default-redis.conf bind port working-directory)))) | |
825 | (list (shepherd-service | |
826 | (provision '(redis)) | |
827 | (documentation "Run the Redis daemon.") | |
828 | (requirement '(user-processes syslogd)) | |
829 | (start #~(make-forkexec-constructor | |
830 | '(#$(file-append redis "/bin/redis-server") | |
831 | #$config-file) | |
832 | #:user "redis" | |
833 | #:group "redis")) | |
834 | (stop #~(make-kill-destructor)))))))) | |
835 | ||
836 | (define redis-service-type | |
837 | (service-type (name 'redis) | |
838 | (extensions | |
839 | (list (service-extension shepherd-root-service-type | |
840 | redis-shepherd-service) | |
841 | (service-extension activation-service-type | |
842 | redis-activation) | |
843 | (service-extension account-service-type | |
bc037c1b CB |
844 | (const %redis-accounts)))) |
845 | (default-value (redis-configuration)))) |