3db9472ee3d0404527c84a103fe1ff711db09dca
[jackhill/guix/guix.git] / gnu / services / databases.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2015 David Thompson <davet@gnu.org>
3 ;;; Copyright © 2015, 2016 Ludovic Courtès <ludo@gnu.org>
4 ;;; Copyright © 2016 Leo Famulari <leo@famulari.name>
5 ;;; Copyright © 2017 Christopher Baines <mail@cbaines.net>
6 ;;; Copyright © 2018 Clément Lassieur <clement@lassieur.org>
7 ;;;
8 ;;; This file is part of GNU Guix.
9 ;;;
10 ;;; GNU Guix is free software; you can redistribute it and/or modify it
11 ;;; under the terms of the GNU General Public License as published by
12 ;;; the Free Software Foundation; either version 3 of the License, or (at
13 ;;; your option) any later version.
14 ;;;
15 ;;; GNU Guix is distributed in the hope that it will be useful, but
16 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;;; GNU General Public License for more details.
19 ;;;
20 ;;; You should have received a copy of the GNU General Public License
21 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
22
23 (define-module (gnu services databases)
24 #:use-module (gnu services)
25 #:use-module (gnu services shepherd)
26 #:use-module (gnu system shadow)
27 #:use-module (gnu packages admin)
28 #:use-module (gnu packages databases)
29 #:use-module (guix modules)
30 #:use-module (guix records)
31 #:use-module (guix gexp)
32 #:use-module (ice-9 match)
33 #:export (postgresql-configuration
34 postgresql-configuration?
35 postgresql-service
36 postgresql-service-type
37
38 memcached-service-type
39 <memcached-configuration>
40 memcached-configuration
41 memcached-configuration?
42 memcached-configuration-memecached
43 memcached-configuration-interfaces
44 memcached-configuration-tcp-port
45 memcached-configuration-udp-port
46 memcached-configuration-additional-options
47
48 <mongodb-configuration>
49 mongodb-configuration
50 mongodb-configuration?
51 mongodb-configuration-mongodb
52 mongodb-configuration-config-file
53 mongodb-configuration-data-directory
54 mongodb-service-type
55
56 mysql-service
57 mysql-service-type
58 mysql-configuration
59 mysql-configuration?
60
61 redis-configuration
62 redis-configuration?
63 redis-service-type))
64
65 ;;; Commentary:
66 ;;;
67 ;;; Database services.
68 ;;;
69 ;;; Code:
70
71 (define-record-type* <postgresql-configuration>
72 postgresql-configuration make-postgresql-configuration
73 postgresql-configuration?
74 (postgresql postgresql-configuration-postgresql ;<package>
75 (default postgresql))
76 (port postgresql-configuration-port
77 (default 5432))
78 (locale postgresql-configuration-locale
79 (default "en_US.utf8"))
80 (config-file postgresql-configuration-file
81 (default %default-postgres-config))
82 (data-directory postgresql-configuration-data-directory
83 (default "/var/lib/postgresql/data")))
84
85 (define %default-postgres-hba
86 (plain-file "pg_hba.conf"
87 "
88 local all all trust
89 host all all 127.0.0.1/32 trust
90 host all all ::1/128 trust"))
91
92 (define %default-postgres-ident
93 (plain-file "pg_ident.conf"
94 "# MAPNAME SYSTEM-USERNAME PG-USERNAME"))
95
96 (define %default-postgres-config
97 (mixed-text-file "postgresql.conf"
98 "log_destination = 'syslog'\n"
99 "hba_file = '" %default-postgres-hba "'\n"
100 "ident_file = '" %default-postgres-ident "'\n"))
101
102 (define %postgresql-accounts
103 (list (user-group (name "postgres") (system? #t))
104 (user-account
105 (name "postgres")
106 (group "postgres")
107 (system? #t)
108 (comment "PostgreSQL server user")
109 (home-directory "/var/empty")
110 (shell (file-append shadow "/sbin/nologin")))))
111
112 (define postgresql-activation
113 (match-lambda
114 (($ <postgresql-configuration> postgresql port locale config-file data-directory)
115 #~(begin
116 (use-modules (guix build utils)
117 (ice-9 match))
118
119 (let ((user (getpwnam "postgres"))
120 (initdb (string-append #$postgresql "/bin/initdb"))
121 (initdb-args
122 (append
123 (if #$locale
124 (list (string-append "--locale=" #$locale))
125 '()))))
126 ;; Create db state directory.
127 (mkdir-p #$data-directory)
128 (chown #$data-directory (passwd:uid user) (passwd:gid user))
129
130 ;; Drop privileges and init state directory in a new
131 ;; process. Wait for it to finish before proceeding.
132 (match (primitive-fork)
133 (0
134 ;; Exit with a non-zero status code if an exception is thrown.
135 (dynamic-wind
136 (const #t)
137 (lambda ()
138 (setgid (passwd:gid user))
139 (setuid (passwd:uid user))
140 (primitive-exit
141 (apply system*
142 initdb
143 "-D"
144 #$data-directory
145 initdb-args)))
146 (lambda ()
147 (primitive-exit 1))))
148 (pid (waitpid pid))))))))
149
150 (define postgresql-shepherd-service
151 (match-lambda
152 (($ <postgresql-configuration> postgresql port locale config-file data-directory)
153 (let* ((pg_ctl-wrapper
154 ;; Wrapper script that switches to the 'postgres' user before
155 ;; launching daemon.
156 (program-file
157 "pg_ctl-wrapper"
158 #~(begin
159 (use-modules (ice-9 match)
160 (ice-9 format))
161 (match (command-line)
162 ((_ mode)
163 (let ((user (getpwnam "postgres"))
164 (pg_ctl #$(file-append postgresql "/bin/pg_ctl"))
165 (options (format #f "--config-file=~a -p ~d"
166 #$config-file #$port)))
167 (setgid (passwd:gid user))
168 (setuid (passwd:uid user))
169 (execl pg_ctl pg_ctl "-D" #$data-directory "-o" options
170 mode)))))))
171 (action (lambda args
172 #~(lambda _
173 (invoke #$pg_ctl-wrapper #$@args)))))
174 (list (shepherd-service
175 (provision '(postgres))
176 (documentation "Run the PostgreSQL daemon.")
177 (requirement '(user-processes loopback syslogd))
178 (start (action "start"))
179 (stop (action "stop"))))))))
180
181 (define postgresql-service-type
182 (service-type (name 'postgresql)
183 (extensions
184 (list (service-extension shepherd-root-service-type
185 postgresql-shepherd-service)
186 (service-extension activation-service-type
187 postgresql-activation)
188 (service-extension account-service-type
189 (const %postgresql-accounts))))
190 (default-value (postgresql-configuration))))
191
192 (define* (postgresql-service #:key (postgresql postgresql)
193 (port 5432)
194 (locale "en_US.utf8")
195 (config-file %default-postgres-config)
196 (data-directory "/var/lib/postgresql/data"))
197 "Return a service that runs @var{postgresql}, the PostgreSQL database server.
198
199 The PostgreSQL daemon loads its runtime configuration from @var{config-file}
200 and stores the database cluster in @var{data-directory}."
201 (service postgresql-service-type
202 (postgresql-configuration
203 (postgresql postgresql)
204 (port port)
205 (locale locale)
206 (config-file config-file)
207 (data-directory data-directory))))
208
209 \f
210 ;;;
211 ;;; Memcached
212 ;;;
213
214 (define-record-type* <memcached-configuration>
215 memcached-configuration make-memcached-configuration
216 memcached-configuration?
217 (memcached memcached-configuration-memcached ;<package>
218 (default memcached))
219 (interfaces memcached-configuration-interfaces
220 (default '("0.0.0.0")))
221 (tcp-port memcached-configuration-tcp-port
222 (default 11211))
223 (udp-port memcached-configuration-udp-port
224 (default 11211))
225 (additional-options memcached-configuration-additional-options
226 (default '())))
227
228 (define %memcached-accounts
229 (list (user-group (name "memcached") (system? #t))
230 (user-account
231 (name "memcached")
232 (group "memcached")
233 (system? #t)
234 (comment "Memcached server user")
235 (home-directory "/var/empty")
236 (shell (file-append shadow "/sbin/nologin")))))
237
238 (define memcached-activation
239 #~(begin
240 (use-modules (guix build utils))
241 (let ((user (getpwnam "memcached")))
242 (mkdir-p "/var/run/memcached")
243 (chown "/var/run/memcached"
244 (passwd:uid user) (passwd:gid user)))))
245
246 (define memcached-shepherd-service
247 (match-lambda
248 (($ <memcached-configuration> memcached interfaces tcp-port udp-port
249 additional-options)
250 (with-imported-modules (source-module-closure
251 '((gnu build shepherd)))
252 (list (shepherd-service
253 (provision '(memcached))
254 (documentation "Run the Memcached daemon.")
255 (requirement '(user-processes loopback))
256 (modules '((gnu build shepherd)))
257 (start #~(make-forkexec-constructor
258 `(#$(file-append memcached "/bin/memcached")
259 "-l" #$(string-join interfaces ",")
260 "-p" #$(number->string tcp-port)
261 "-U" #$(number->string udp-port)
262 "--daemon"
263 ;; Memcached changes to the memcached user prior to
264 ;; writing the pid file, so write it to a directory
265 ;; that memcached owns.
266 "-P" "/var/run/memcached/pid"
267 "-u" "memcached"
268 ,#$@additional-options)
269 #:log-file "/var/log/memcached"
270 #:pid-file "/var/run/memcached/pid"))
271 (stop #~(make-kill-destructor))))))))
272
273 (define memcached-service-type
274 (service-type (name 'memcached)
275 (extensions
276 (list (service-extension shepherd-root-service-type
277 memcached-shepherd-service)
278 (service-extension activation-service-type
279 (const memcached-activation))
280 (service-extension account-service-type
281 (const %memcached-accounts))))
282 (default-value (memcached-configuration))))
283
284 \f
285 ;;;
286 ;;; MongoDB
287 ;;;
288
289 (define %default-mongodb-configuration-file
290 (plain-file
291 "mongodb.yaml"
292 "# GNU Guix: MongoDB default configuration file
293 processManagement:
294 pidFilePath: /var/run/mongodb/pid
295 storage:
296 dbPath: /var/lib/mongodb
297 "))
298
299
300 (define-record-type* <mongodb-configuration>
301 mongodb-configuration make-mongodb-configuration
302 mongodb-configuration?
303 (mongodb mongodb-configuration-mongodb
304 (default mongodb))
305 (config-file mongodb-configuration-config-file
306 (default %default-mongodb-configuration-file))
307 (data-directory mongodb-configuration-data-directory
308 (default "/var/lib/mongodb")))
309
310 (define %mongodb-accounts
311 (list (user-group (name "mongodb") (system? #t))
312 (user-account
313 (name "mongodb")
314 (group "mongodb")
315 (system? #t)
316 (comment "Mongodb server user")
317 (home-directory "/var/lib/mongodb")
318 (shell (file-append shadow "/sbin/nologin")))))
319
320 (define mongodb-activation
321 (match-lambda
322 (($ <mongodb-configuration> mongodb config-file data-directory)
323 #~(begin
324 (use-modules (guix build utils))
325 (let ((user (getpwnam "mongodb")))
326 (for-each
327 (lambda (directory)
328 (mkdir-p directory)
329 (chown directory
330 (passwd:uid user) (passwd:gid user)))
331 '("/var/run/mongodb" #$data-directory)))))))
332
333 (define mongodb-shepherd-service
334 (match-lambda
335 (($ <mongodb-configuration> mongodb config-file data-directory)
336 (shepherd-service
337 (provision '(mongodb))
338 (documentation "Run the Mongodb daemon.")
339 (requirement '(user-processes loopback))
340 (start #~(make-forkexec-constructor
341 `(,(string-append #$mongodb "/bin/mongod")
342 "--config"
343 ,#$config-file)
344 #:user "mongodb"
345 #:group "mongodb"
346 #:pid-file "/var/run/mongodb/pid"
347 #:log-file "/var/log/mongodb.log"))
348 (stop #~(make-kill-destructor))))))
349
350 (define mongodb-service-type
351 (service-type
352 (name 'mongodb)
353 (description "Run the MongoDB document database server.")
354 (extensions
355 (list (service-extension shepherd-root-service-type
356 (compose list
357 mongodb-shepherd-service))
358 (service-extension activation-service-type
359 mongodb-activation)
360 (service-extension account-service-type
361 (const %mongodb-accounts))))
362 (default-value
363 (mongodb-configuration))))
364
365 \f
366 ;;;
367 ;;; MySQL.
368 ;;;
369
370 (define-record-type* <mysql-configuration>
371 mysql-configuration make-mysql-configuration
372 mysql-configuration?
373 (mysql mysql-configuration-mysql (default mariadb))
374 (port mysql-configuration-port (default 3306)))
375
376 (define %mysql-accounts
377 (list (user-group
378 (name "mysql")
379 (system? #t))
380 (user-account
381 (name "mysql")
382 (group "mysql")
383 (system? #t)
384 (home-directory "/var/empty")
385 (shell (file-append shadow "/sbin/nologin")))))
386
387 (define mysql-configuration-file
388 (match-lambda
389 (($ <mysql-configuration> mysql port)
390 (mixed-text-file "my.cnf" "[mysqld]
391 datadir=/var/lib/mysql
392 socket=/run/mysqld/mysqld.sock
393 port=" (number->string port) "
394 "))))
395
396 (define (%mysql-activation config)
397 "Return an activation gexp for the MySQL or MariaDB database server."
398 (let ((mysql (mysql-configuration-mysql config))
399 (my.cnf (mysql-configuration-file config)))
400 #~(begin
401 (use-modules (ice-9 popen)
402 (guix build utils))
403 (let* ((mysqld (string-append #$mysql "/bin/mysqld"))
404 (user (getpwnam "mysql"))
405 (uid (passwd:uid user))
406 (gid (passwd:gid user))
407 (datadir "/var/lib/mysql")
408 (rundir "/run/mysqld"))
409 (mkdir-p datadir)
410 (chown datadir uid gid)
411 (mkdir-p rundir)
412 (chown rundir uid gid)
413 ;; Initialize the database when it doesn't exist.
414 (when (not (file-exists? (string-append datadir "/mysql")))
415 (if (string-prefix? "mysql-" (strip-store-file-name #$mysql))
416 ;; For MySQL.
417 (system* mysqld
418 (string-append "--defaults-file=" #$my.cnf)
419 "--initialize"
420 "--user=mysql")
421 ;; For MariaDB.
422 ;; XXX: The 'mysql_install_db' script doesn't work directly
423 ;; due to missing 'mkdir' in PATH.
424 (let ((p (open-pipe* OPEN_WRITE mysqld
425 (string-append
426 "--defaults-file=" #$my.cnf)
427 "--bootstrap"
428 "--user=mysql")))
429 ;; Create the system database, as does by 'mysql_install_db'.
430 (display "create database mysql;\n" p)
431 (display "use mysql;\n" p)
432 (for-each
433 (lambda (sql)
434 (call-with-input-file
435 (string-append #$mysql "/share/mysql/" sql)
436 (lambda (in) (dump-port in p))))
437 '("mysql_system_tables.sql"
438 "mysql_performance_tables.sql"
439 "mysql_system_tables_data.sql"
440 "fill_help_tables.sql"))
441 ;; Remove the anonymous user and disable root access from
442 ;; remote machines, as does by 'mysql_secure_installation'.
443 (display "
444 DELETE FROM user WHERE User='';
445 DELETE FROM user WHERE User='root' AND
446 Host NOT IN ('localhost', '127.0.0.1', '::1');
447 FLUSH PRIVILEGES;
448 " p)
449 (close-pipe p))))))))
450
451 (define (mysql-shepherd-service config)
452 (list (shepherd-service
453 (provision '(mysql))
454 (documentation "Run the MySQL server.")
455 (start (let ((mysql (mysql-configuration-mysql config))
456 (my.cnf (mysql-configuration-file config)))
457 #~(make-forkexec-constructor
458 (list (string-append #$mysql "/bin/mysqld")
459 (string-append "--defaults-file=" #$my.cnf))
460 #:user "mysql" #:group "mysql")))
461 (stop #~(make-kill-destructor)))))
462
463 (define mysql-service-type
464 (service-type
465 (name 'mysql)
466 (extensions
467 (list (service-extension account-service-type
468 (const %mysql-accounts))
469 (service-extension activation-service-type
470 %mysql-activation)
471 (service-extension shepherd-root-service-type
472 mysql-shepherd-service)))))
473
474 (define* (mysql-service #:key (config (mysql-configuration)))
475 "Return a service that runs @command{mysqld}, the MySQL or MariaDB
476 database server.
477
478 The optional @var{config} argument specifies the configuration for
479 @command{mysqld}, which should be a @code{<mysql-configuration>} object."
480 (service mysql-service-type config))
481
482 \f
483 ;;;
484 ;;; Redis
485 ;;;
486
487 (define-record-type* <redis-configuration>
488 redis-configuration make-redis-configuration
489 redis-configuration?
490 (redis redis-configuration-redis ;<package>
491 (default redis))
492 (bind redis-configuration-bind
493 (default "127.0.0.1"))
494 (port redis-configuration-port
495 (default 6379))
496 (working-directory redis-configuration-working-directory
497 (default "/var/lib/redis"))
498 (config-file redis-configuration-config-file
499 (default #f)))
500
501 (define (default-redis.conf bind port working-directory)
502 (mixed-text-file "redis.conf"
503 "bind " bind "\n"
504 "port " (number->string port) "\n"
505 "dir " working-directory "\n"
506 "daemonize no\n"))
507
508 (define %redis-accounts
509 (list (user-group (name "redis") (system? #t))
510 (user-account
511 (name "redis")
512 (group "redis")
513 (system? #t)
514 (comment "Redis server user")
515 (home-directory "/var/empty")
516 (shell (file-append shadow "/sbin/nologin")))))
517
518 (define redis-activation
519 (match-lambda
520 (($ <redis-configuration> redis bind port working-directory config-file)
521 #~(begin
522 (use-modules (guix build utils)
523 (ice-9 match))
524
525 (let ((user (getpwnam "redis")))
526 (mkdir-p #$working-directory)
527 (chown #$working-directory (passwd:uid user) (passwd:gid user)))))))
528
529 (define redis-shepherd-service
530 (match-lambda
531 (($ <redis-configuration> redis bind port working-directory config-file)
532 (let ((config-file
533 (or config-file
534 (default-redis.conf bind port working-directory))))
535 (list (shepherd-service
536 (provision '(redis))
537 (documentation "Run the Redis daemon.")
538 (requirement '(user-processes syslogd))
539 (start #~(make-forkexec-constructor
540 '(#$(file-append redis "/bin/redis-server")
541 #$config-file)
542 #:user "redis"
543 #:group "redis"))
544 (stop #~(make-kill-destructor))))))))
545
546 (define redis-service-type
547 (service-type (name 'redis)
548 (extensions
549 (list (service-extension shepherd-root-service-type
550 redis-shepherd-service)
551 (service-extension activation-service-type
552 redis-activation)
553 (service-extension account-service-type
554 (const %redis-accounts))))))