gnu: bootstrap: Memoize 'bootstrap-origin'.
[jackhill/guix/guix.git] / gnu / services / web.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2015 David Thompson <davet@gnu.org>
3 ;;; Copyright © 2015, 2016, 2017, 2018 Ludovic Courtès <ludo@gnu.org>
4 ;;; Copyright © 2016 ng0 <ng0@n0.is>
5 ;;; Copyright © 2016, 2017, 2018 Julien Lepiller <julien@lepiller.eu>
6 ;;; Copyright © 2017 Christopher Baines <mail@cbaines.net>
7 ;;; Copyright © 2017 nee <nee-git@hidamari.blue>
8 ;;; Copyright © 2017, 2018 Clément Lassieur <clement@lassieur.org>
9 ;;; Copyright © 2018 Pierre-Antoine Rouby <pierre-antoine.rouby@inria.fr>
10 ;;; Copyright © 2017, 2018, 2019 Christopher Baines <mail@cbaines.net>
11 ;;; Copyright © 2018 Marius Bakke <mbakke@fastmail.com>
12 ;;;
13 ;;; This file is part of GNU Guix.
14 ;;;
15 ;;; GNU Guix is free software; you can redistribute it and/or modify it
16 ;;; under the terms of the GNU General Public License as published by
17 ;;; the Free Software Foundation; either version 3 of the License, or (at
18 ;;; your option) any later version.
19 ;;;
20 ;;; GNU Guix is distributed in the hope that it will be useful, but
21 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
22 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 ;;; GNU General Public License for more details.
24 ;;;
25 ;;; You should have received a copy of the GNU General Public License
26 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
27
28 (define-module (gnu services web)
29 #:use-module (gnu services)
30 #:use-module (gnu services shepherd)
31 #:use-module (gnu services admin)
32 #:use-module (gnu services getmail)
33 #:use-module (gnu services mail)
34 #:use-module (gnu system pam)
35 #:use-module (gnu system shadow)
36 #:use-module (gnu packages admin)
37 #:use-module (gnu packages databases)
38 #:use-module (gnu packages web)
39 #:use-module (gnu packages patchutils)
40 #:use-module (gnu packages php)
41 #:use-module (gnu packages python)
42 #:use-module (gnu packages gnupg)
43 #:use-module (gnu packages guile)
44 #:use-module (gnu packages logging)
45 #:use-module (guix packages)
46 #:use-module (guix records)
47 #:use-module (guix modules)
48 #:use-module (guix utils)
49 #:use-module (guix gexp)
50 #:use-module ((guix store) #:select (text-file))
51 #:use-module ((guix utils) #:select (version-major))
52 #:use-module ((guix packages) #:select (package-version))
53 #:use-module (srfi srfi-1)
54 #:use-module (srfi srfi-9)
55 #:use-module (ice-9 match)
56 #:export (<httpd-configuration>
57 httpd-configuration
58 httpd-configuration?
59 httpd-configuration-package
60 httpd-configuration-pid-file
61 httpd-configuration-config
62
63 <httpd-virtualhost>
64 httpd-virtualhost
65 httpd-virtualhost?
66 httpd-virtualhost-addresses-and-ports
67 httpd-virtualhost-contents
68
69 <httpd-config-file>
70 httpd-config-file
71 httpd-config-file?
72 httpd-config-file-modules
73 httpd-config-file-server-root
74 httpd-config-file-server-name
75 httpd-config-file-listen
76 httpd-config-file-pid-file
77 httpd-config-file-error-log
78 httpd-config-file-user
79 httpd-config-file-group
80
81 <httpd-module>
82 httpd-module
83 httpd-module?
84 %default-httpd-modules
85
86 httpd-service-type
87
88 <nginx-configuration>
89 nginx-configuration
90 nginx-configuration?
91 nginx-configuartion-nginx
92 nginx-configuration-log-directory
93 nginx-configuration-run-directory
94 nginx-configuration-server-blocks
95 nginx-configuration-upstream-blocks
96 nginx-configuration-server-names-hash-bucket-size
97 nginx-configuration-server-names-hash-bucket-max-size
98 nginx-configuration-extra-content
99 nginx-configuration-file
100
101 <nginx-server-configuration>
102 nginx-server-configuration
103 nginx-server-configuration?
104 nginx-server-configuration-listen
105 nginx-server-configuration-server-name
106 nginx-server-configuration-root
107 nginx-server-configuration-locations
108 nginx-server-configuration-index
109 nginx-server-configuration-ssl-certificate
110 nginx-server-configuration-ssl-certificate-key
111 nginx-server-configuration-server-tokens?
112 nginx-server-configuration-raw-content
113
114 <nginx-upstream-configuration>
115 nginx-upstream-configuration
116 nginx-upstream-configuration?
117 nginx-upstream-configuration-name
118 nginx-upstream-configuration-servers
119
120 <nginx-location-configuration>
121 nginx-location-configuration
122 nginx-location-configuration?
123 nginx-location-configuration-uri
124 nginx-location-configuration-body
125
126 <nginx-named-location-configuration>
127 nginx-named-location-configuration
128 nginx-named-location-configuration?
129 nginx-named-location-configuration-name
130 nginx-named-location-configuration-body
131
132 nginx-service
133 nginx-service-type
134
135 fcgiwrap-configuration
136 fcgiwrap-configuration?
137 fcgiwrap-service-type
138
139 <php-fpm-configuration>
140 php-fpm-configuration
141 make-php-fpm-configuration
142 php-fpm-configuration?
143 php-fpm-configuration-php
144 php-fpm-configuration-socket
145 php-fpm-configuration-user
146 php-fpm-configuration-group
147 php-fpm-configuration-socket-user
148 php-fpm-configuration-socket-group
149 php-fpm-configuration-pid-file
150 php-fpm-configuration-log-file
151 php-fpm-configuration-process-manager
152 php-fpm-configuration-display-errors
153 php-fpm-configuration-timezone
154 php-fpm-configuration-workers-log-file
155 php-fpm-configuration-file
156
157 <php-fpm-dynamic-process-manager-configuration>
158 php-fpm-dynamic-process-manager-configuration
159 make-php-fpm-dynamic-process-manager-configuration
160 php-fpm-dynamic-process-manager-configuration?
161 php-fpm-dynamic-process-manager-configuration-max-children
162 php-fpm-dynamic-process-manager-configuration-start-servers
163 php-fpm-dynamic-process-manager-configuration-min-spare-servers
164 php-fpm-dynamic-process-manager-configuration-max-spare-servers
165
166 <php-fpm-static-process-manager-configuration>
167 php-fpm-static-process-manager-configuration
168 make-php-fpm-static-process-manager-configuration
169 php-fpm-static-process-manager-configuration?
170 php-fpm-static-process-manager-configuration-max-children
171
172 <php-fpm-on-demand-process-manager-configuration>
173 php-fpm-on-demand-process-manager-configuration
174 make-php-fpm-on-demand-process-manager-configuration
175 php-fpm-on-demand-process-manager-configuration?
176 php-fpm-on-demand-process-manager-configuration-max-children
177 php-fpm-on-demand-process-manager-configuration-process-idle-timeout
178
179 php-fpm-service-type
180 nginx-php-location
181
182 cat-avatar-generator-service
183
184 hpcguix-web-configuration
185 hpcguix-web-configuration?
186 hpcguix-web-service-type
187
188 <tailon-configuration-file>
189 tailon-configuration-file
190 tailon-configuration-file?
191 tailon-configuration-file-files
192 tailon-configuration-file-bind
193 tailon-configuration-file-relative-root
194 tailon-configuration-file-allow-transfers?
195 tailon-configuration-file-follow-names?
196 tailon-configuration-file-tail-lines
197 tailon-configuration-file-allowed-commands
198 tailon-configuration-file-debug?
199 tailon-configuration-file-http-auth
200 tailon-configuration-file-users
201
202 <tailon-configuration>
203 tailon-configuration
204 tailon-configuration?
205 tailon-configuration-config-file
206 tailon-configuration-package
207
208 tailon-service-type
209
210 <varnish-configuration>
211 varnish-configuration
212 varnish-configuration?
213 varnish-configuration-package
214 varnish-configuration-name
215 varnish-configuration-backend
216 varnish-configuration-vcl
217 varnish-configuration-listen
218 varnish-configuration-storage
219 varnish-configuration-parameters
220 varnish-configuration-extra-options
221
222 varnish-service-type
223
224 <patchwork-database-configuration>
225 patchwork-database-configuration
226 patchwork-database-configuration?
227 patchwork-database-configuration-engine
228 patchwork-database-configuration-name
229 patchwork-database-configuration-user
230 patchwork-database-configuration-password
231 patchwork-database-configuration-host
232 patchwork-database-configuration-port
233
234 <patchwork-settings-module>
235 patchwork-settings-module
236 patchwork-settings-module?
237 patchwork-settings-module-database-configuration
238 patchwork-settings-module-secret-key
239 patchwork-settings-module-allowed-hosts
240 patchwork-settings-module-default-from-email
241 patchwork-settings-module-static-url
242 patchwork-settings-module-admins
243 patchwork-settings-module-debug?
244 patchwork-settings-module-enable-rest-api?
245 patchwork-settings-module-enable-xmlrpc?
246 patchwork-settings-module-force-https-links?
247 patchwork-settings-module-extra-settings
248
249 <patchwork-configuration>
250 patchwork-configuration
251 patchwork-configuration?
252 patchwork-configuration-patchwork
253 patchwork-configuration-settings-module
254 patchwork-configuration-domain
255
256 patchwork-virtualhost
257 patchwork-service-type))
258
259 ;;; Commentary:
260 ;;;
261 ;;; Web services.
262 ;;;
263 ;;; Code:
264
265 (define-record-type* <httpd-module>
266 httpd-module make-httpd-module
267 httpd-module?
268 (name httpd-load-module-name)
269 (file httpd-load-module-file))
270
271 ;; Default modules for the httpd-service-type, taken from etc/httpd/httpd.conf
272 ;; file in the httpd package.
273 (define %default-httpd-modules
274 (map (match-lambda
275 ((name file)
276 (httpd-module
277 (name name)
278 (file file))))
279 '(("authn_file_module" "modules/mod_authn_file.so")
280 ("authn_core_module" "modules/mod_authn_core.so")
281 ("authz_host_module" "modules/mod_authz_host.so")
282 ("authz_groupfile_module" "modules/mod_authz_groupfile.so")
283 ("authz_user_module" "modules/mod_authz_user.so")
284 ("authz_core_module" "modules/mod_authz_core.so")
285 ("access_compat_module" "modules/mod_access_compat.so")
286 ("auth_basic_module" "modules/mod_auth_basic.so")
287 ("reqtimeout_module" "modules/mod_reqtimeout.so")
288 ("filter_module" "modules/mod_filter.so")
289 ("mime_module" "modules/mod_mime.so")
290 ("log_config_module" "modules/mod_log_config.so")
291 ("env_module" "modules/mod_env.so")
292 ("headers_module" "modules/mod_headers.so")
293 ("setenvif_module" "modules/mod_setenvif.so")
294 ("version_module" "modules/mod_version.so")
295 ("unixd_module" "modules/mod_unixd.so")
296 ("status_module" "modules/mod_status.so")
297 ("autoindex_module" "modules/mod_autoindex.so")
298 ("dir_module" "modules/mod_dir.so")
299 ("alias_module" "modules/mod_alias.so"))))
300
301 (define-record-type* <httpd-config-file>
302 httpd-config-file make-httpd-config-file
303 httpd-config-file?
304 (modules httpd-config-file-modules
305 (default %default-httpd-modules))
306 (server-root httpd-config-file-server-root
307 (default httpd))
308 (server-name httpd-config-file-server-name
309 (default #f))
310 (document-root httpd-config-file-document-root
311 (default "/srv/http"))
312 (listen httpd-config-file-listen
313 (default '("80")))
314 (pid-file httpd-config-file-pid-file
315 (default "/var/run/httpd"))
316 (error-log httpd-config-file-error-log
317 (default "/var/log/httpd/error_log"))
318 (user httpd-config-file-user
319 (default "httpd"))
320 (group httpd-config-file-group
321 (default "httpd"))
322 (extra-config httpd-config-file-extra-config
323 (default
324 (list "TypesConfig etc/httpd/mime.types"))))
325
326 (define-gexp-compiler (httpd-config-file-compiler
327 (file <httpd-config-file>) system target)
328 (match file
329 (($ <httpd-config-file> load-modules server-root server-name
330 document-root listen pid-file error-log
331 user group extra-config)
332 (gexp->derivation
333 "httpd.conf"
334 #~(call-with-output-file (ungexp output "out")
335 (lambda (port)
336 (display
337 (string-append
338 (ungexp-splicing
339 `(,@(append-map
340 (match-lambda
341 (($ <httpd-module> name module)
342 `("LoadModule " ,name " " ,module "\n")))
343 load-modules)
344 ,@`("ServerRoot " ,server-root "\n")
345 ,@(if server-name
346 `("ServerName " ,server-name "\n")
347 '())
348 ,@`("DocumentRoot " ,document-root "\n")
349 ,@(append-map
350 (lambda (listen-value)
351 `("Listen " ,listen-value "\n"))
352 listen)
353 ,@(if pid-file
354 `("Pidfile " ,pid-file "\n")
355 '())
356 ,@(if error-log
357 `("ErrorLog " ,error-log "\n")
358 '())
359 ,@(if user
360 `("User " ,user "\n")
361 '())
362 ,@(if group
363 `("Group " ,group "\n")
364 '())
365 "\n\n"
366 ,@extra-config)))
367 port)))
368 #:local-build? #t))))
369
370 (define-record-type <httpd-virtualhost>
371 (httpd-virtualhost addresses-and-ports contents)
372 httpd-virtualhost?
373 (addresses-and-ports httpd-virtualhost-addresses-and-ports)
374 (contents httpd-virtualhost-contents))
375
376 (define-record-type* <httpd-configuration>
377 httpd-configuration make-httpd-configuration
378 httpd-configuration?
379 (package httpd-configuration-package
380 (default httpd))
381 (pid-file httpd-configuration-pid-file
382 (default "/var/run/httpd"))
383 (config httpd-configuration-config
384 (default (httpd-config-file))))
385
386 (define %httpd-accounts
387 (list (user-group (name "httpd") (system? #t))
388 (user-account
389 (name "httpd")
390 (group "httpd")
391 (system? #t)
392 (comment "Apache HTTPD server user")
393 (home-directory "/var/empty")
394 (shell (file-append shadow "/sbin/nologin")))))
395
396 (define httpd-shepherd-services
397 (match-lambda
398 (($ <httpd-configuration> package pid-file config)
399 (list (shepherd-service
400 (provision '(httpd))
401 (documentation "The Apache HTTP Server")
402 (requirement '(networking))
403 (start #~(make-forkexec-constructor
404 `(#$(file-append package "/bin/httpd")
405 #$@(if config
406 (list "-f" config)
407 '()))
408 #:pid-file #$pid-file))
409 (stop #~(make-kill-destructor)))))))
410
411 (define httpd-activation
412 (match-lambda
413 (($ <httpd-configuration> package pid-file config)
414 (match-record
415 config
416 <httpd-config-file>
417 (error-log document-root)
418 #~(begin
419 (use-modules (guix build utils))
420
421 (mkdir-p #$(dirname error-log))
422 (mkdir-p #$document-root))))))
423
424 (define (httpd-process-extensions original-config extension-configs)
425 (let ((config (httpd-configuration-config
426 original-config)))
427 (if (httpd-config-file? config)
428 (httpd-configuration
429 (inherit original-config)
430 (config
431 (httpd-config-file
432 (inherit config)
433 (extra-config
434 (append (httpd-config-file-extra-config config)
435 (append-map
436 (match-lambda
437 (($ <httpd-virtualhost>
438 addresses-and-ports
439 contents)
440 `(,(string-append
441 "\n<VirtualHost " addresses-and-ports ">\n")
442 ,@contents
443 "\n</VirtualHost>\n"))
444 ((? string? x)
445 `("\n" ,x "\n"))
446 ((? list? x)
447 `("\n" ,@x "\n")))
448 extension-configs)))))))))
449
450 (define httpd-service-type
451 (service-type (name 'httpd)
452 (extensions
453 (list (service-extension shepherd-root-service-type
454 httpd-shepherd-services)
455 (service-extension activation-service-type
456 httpd-activation)
457 (service-extension account-service-type
458 (const %httpd-accounts))))
459 (compose concatenate)
460 (extend httpd-process-extensions)
461 (default-value
462 (httpd-configuration))))
463
464 (define-record-type* <nginx-server-configuration>
465 nginx-server-configuration make-nginx-server-configuration
466 nginx-server-configuration?
467 (listen nginx-server-configuration-listen
468 (default '("80" "443 ssl")))
469 (server-name nginx-server-configuration-server-name
470 (default (list 'default)))
471 (root nginx-server-configuration-root
472 (default "/srv/http"))
473 (locations nginx-server-configuration-locations
474 (default '()))
475 (index nginx-server-configuration-index
476 (default (list "index.html")))
477 (try-files nginx-server-configuration-try-files
478 (default '()))
479 (ssl-certificate nginx-server-configuration-ssl-certificate
480 (default #f))
481 (ssl-certificate-key nginx-server-configuration-ssl-certificate-key
482 (default #f))
483 (server-tokens? nginx-server-configuration-server-tokens?
484 (default #f))
485 (raw-content nginx-server-configuration-raw-content
486 (default '())))
487
488 (define-record-type* <nginx-upstream-configuration>
489 nginx-upstream-configuration make-nginx-upstream-configuration
490 nginx-upstream-configuration?
491 (name nginx-upstream-configuration-name)
492 (servers nginx-upstream-configuration-servers))
493
494 (define-record-type* <nginx-location-configuration>
495 nginx-location-configuration make-nginx-location-configuration
496 nginx-location-configuration?
497 (uri nginx-location-configuration-uri
498 (default #f))
499 (body nginx-location-configuration-body))
500
501 (define-record-type* <nginx-named-location-configuration>
502 nginx-named-location-configuration make-nginx-named-location-configuration
503 nginx-named-location-configuration?
504 (name nginx-named-location-configuration-name
505 (default #f))
506 (body nginx-named-location-configuration-body))
507
508 (define-record-type* <nginx-configuration>
509 nginx-configuration make-nginx-configuration
510 nginx-configuration?
511 (nginx nginx-configuration-nginx ;<package>
512 (default nginx))
513 (log-directory nginx-configuration-log-directory ;string
514 (default "/var/log/nginx"))
515 (run-directory nginx-configuration-run-directory ;string
516 (default "/var/run/nginx"))
517 (server-blocks nginx-configuration-server-blocks
518 (default '())) ;list of <nginx-server-configuration>
519 (upstream-blocks nginx-configuration-upstream-blocks
520 (default '())) ;list of <nginx-upstream-configuration>
521 (server-names-hash-bucket-size nginx-configuration-server-names-hash-bucket-size
522 (default #f))
523 (server-names-hash-bucket-max-size nginx-configuration-server-names-hash-bucket-max-size
524 (default #f))
525 (extra-content nginx-configuration-extra-content
526 (default ""))
527 (file nginx-configuration-file ;#f | string | file-like
528 (default #f)))
529
530 (define (config-domain-strings names)
531 "Return a string denoting the nginx config representation of NAMES, a list
532 of domain names."
533 (map (match-lambda
534 ('default "_ ")
535 ((? string? str) (list str " ")))
536 names))
537
538 (define (config-index-strings names)
539 "Return a string denoting the nginx config representation of NAMES, a list
540 of index files."
541 (map (match-lambda
542 ((? string? str) (list str " ")))
543 names))
544
545 (define emit-nginx-location-config
546 (match-lambda
547 (($ <nginx-location-configuration> uri body)
548 (list
549 " location " uri " {\n"
550 (map (lambda (x) (list " " x "\n")) body)
551 " }\n"))
552 (($ <nginx-named-location-configuration> name body)
553 (list
554 " location @" name " {\n"
555 (map (lambda (x) (list " " x "\n")) body)
556 " }\n"))))
557
558 (define (emit-nginx-server-config server)
559 (let ((listen (nginx-server-configuration-listen server))
560 (server-name (nginx-server-configuration-server-name server))
561 (ssl-certificate (nginx-server-configuration-ssl-certificate server))
562 (ssl-certificate-key
563 (nginx-server-configuration-ssl-certificate-key server))
564 (root (nginx-server-configuration-root server))
565 (index (nginx-server-configuration-index server))
566 (try-files (nginx-server-configuration-try-files server))
567 (server-tokens? (nginx-server-configuration-server-tokens? server))
568 (locations (nginx-server-configuration-locations server))
569 (raw-content (nginx-server-configuration-raw-content server)))
570 (define-syntax-parameter <> (syntax-rules ()))
571 (define-syntax-rule (and/l x tail ...)
572 (let ((x* x))
573 (if x*
574 (syntax-parameterize ((<> (identifier-syntax x*)))
575 (list tail ...))
576 '())))
577 (list
578 " server {\n"
579 (map (lambda (directive) (list " listen " directive ";\n")) listen)
580 " server_name " (config-domain-strings server-name) ";\n"
581 (and/l ssl-certificate " ssl_certificate " <> ";\n")
582 (and/l ssl-certificate-key " ssl_certificate_key " <> ";\n")
583 " root " root ";\n"
584 " index " (config-index-strings index) ";\n"
585 (if (not (nil? try-files))
586 (and/l (config-index-strings try-files) " try_files " <> ";\n")
587 "")
588 " server_tokens " (if server-tokens? "on" "off") ";\n"
589 "\n"
590 (map emit-nginx-location-config locations)
591 "\n"
592 (map (lambda (x) (list " " x "\n")) raw-content)
593 " }\n")))
594
595 (define (emit-nginx-upstream-config upstream)
596 (list
597 " upstream " (nginx-upstream-configuration-name upstream) " {\n"
598 (map (lambda (server)
599 (simple-format #f " server ~A;\n" server))
600 (nginx-upstream-configuration-servers upstream))
601 " }\n"))
602
603 (define (flatten . lst)
604 "Return a list that recursively concatenates all sub-lists of LST."
605 (define (flatten1 head out)
606 (if (list? head)
607 (fold-right flatten1 out head)
608 (cons head out)))
609 (fold-right flatten1 '() lst))
610
611 (define (default-nginx-config config)
612 (match-record config
613 <nginx-configuration>
614 (nginx log-directory run-directory
615 server-blocks upstream-blocks
616 server-names-hash-bucket-size
617 server-names-hash-bucket-max-size
618 extra-content)
619 (apply mixed-text-file "nginx.conf"
620 (flatten
621 "user nginx nginx;\n"
622 "pid " run-directory "/pid;\n"
623 "error_log " log-directory "/error.log info;\n"
624 "http {\n"
625 " client_body_temp_path " run-directory "/client_body_temp;\n"
626 " proxy_temp_path " run-directory "/proxy_temp;\n"
627 " fastcgi_temp_path " run-directory "/fastcgi_temp;\n"
628 " uwsgi_temp_path " run-directory "/uwsgi_temp;\n"
629 " scgi_temp_path " run-directory "/scgi_temp;\n"
630 " access_log " log-directory "/access.log;\n"
631 " include " nginx "/share/nginx/conf/mime.types;\n"
632 (if server-names-hash-bucket-size
633 (string-append
634 " server_names_hash_bucket_size "
635 (number->string server-names-hash-bucket-size)
636 ";\n")
637 "")
638 (if server-names-hash-bucket-max-size
639 (string-append
640 " server_names_hash_bucket_max_size "
641 (number->string server-names-hash-bucket-max-size)
642 ";\n")
643 "")
644 "\n"
645 (map emit-nginx-upstream-config upstream-blocks)
646 (map emit-nginx-server-config server-blocks)
647 extra-content
648 "\n}\n"
649 "events {}\n"))))
650
651 (define %nginx-accounts
652 (list (user-group (name "nginx") (system? #t))
653 (user-account
654 (name "nginx")
655 (group "nginx")
656 (system? #t)
657 (comment "nginx server user")
658 (home-directory "/var/empty")
659 (shell (file-append shadow "/sbin/nologin")))))
660
661 (define (nginx-activation config)
662 (match-record config
663 <nginx-configuration>
664 (nginx log-directory run-directory file)
665 #~(begin
666 (use-modules (guix build utils))
667
668 (format #t "creating nginx log directory '~a'~%" #$log-directory)
669 (mkdir-p #$log-directory)
670 (format #t "creating nginx run directory '~a'~%" #$run-directory)
671 (mkdir-p #$run-directory)
672 (format #t "creating nginx temp directories '~a/{client_body,proxy,fastcgi,uwsgi,scgi}_temp'~%" #$run-directory)
673 (mkdir-p (string-append #$run-directory "/client_body_temp"))
674 (mkdir-p (string-append #$run-directory "/proxy_temp"))
675 (mkdir-p (string-append #$run-directory "/fastcgi_temp"))
676 (mkdir-p (string-append #$run-directory "/uwsgi_temp"))
677 (mkdir-p (string-append #$run-directory "/scgi_temp"))
678 ;; Start-up logs. Once configuration is loaded, nginx switches to
679 ;; log-directory.
680 (mkdir-p (string-append #$run-directory "/logs"))
681 ;; Check configuration file syntax.
682 (system* (string-append #$nginx "/sbin/nginx")
683 "-c" #$(or file
684 (default-nginx-config config))
685 "-p" #$run-directory
686 "-t"))))
687
688 (define (nginx-shepherd-service config)
689 (match-record config
690 <nginx-configuration>
691 (nginx file run-directory)
692 (let* ((nginx-binary (file-append nginx "/sbin/nginx"))
693 (pid-file (in-vicinity run-directory "pid"))
694 (nginx-action
695 (lambda args
696 #~(lambda _
697 (invoke #$nginx-binary "-c"
698 #$(or file
699 (default-nginx-config config))
700 #$@args)
701 (match '#$args
702 (("-s" . _) #f)
703 (_
704 ;; When FILE is true, we cannot be sure that PID-FILE will
705 ;; be created, so assume it won't show up. When FILE is
706 ;; false, read PID-FILE.
707 #$(if file
708 #~#t
709 #~(read-pid-file #$pid-file))))))))
710
711 ;; TODO: Add 'reload' action.
712 (list (shepherd-service
713 (provision '(nginx))
714 (documentation "Run the nginx daemon.")
715 (requirement '(user-processes loopback))
716 (modules `((ice-9 match)
717 ,@%default-modules))
718 (start (nginx-action "-p" run-directory))
719 (stop (nginx-action "-s" "stop")))))))
720
721 (define nginx-service-type
722 (service-type (name 'nginx)
723 (extensions
724 (list (service-extension shepherd-root-service-type
725 nginx-shepherd-service)
726 (service-extension activation-service-type
727 nginx-activation)
728 (service-extension account-service-type
729 (const %nginx-accounts))))
730 (compose concatenate)
731 (extend (lambda (config servers)
732 (nginx-configuration
733 (inherit config)
734 (server-blocks
735 (append (nginx-configuration-server-blocks config)
736 servers)))))
737 (default-value
738 (nginx-configuration))))
739
740 (define-record-type* <fcgiwrap-configuration> fcgiwrap-configuration
741 make-fcgiwrap-configuration
742 fcgiwrap-configuration?
743 (package fcgiwrap-configuration-package ;<package>
744 (default fcgiwrap))
745 (socket fcgiwrap-configuration-socket
746 (default "tcp:127.0.0.1:9000"))
747 (user fcgiwrap-configuration-user
748 (default "fcgiwrap"))
749 (group fcgiwrap-configuration-group
750 (default "fcgiwrap")))
751
752 (define fcgiwrap-accounts
753 (match-lambda
754 (($ <fcgiwrap-configuration> package socket user group)
755 (filter identity
756 (list
757 (and (equal? group "fcgiwrap")
758 (user-group
759 (name "fcgiwrap")
760 (system? #t)))
761 (and (equal? user "fcgiwrap")
762 (user-account
763 (name "fcgiwrap")
764 (group group)
765 (system? #t)
766 (comment "Fcgiwrap Daemon")
767 (home-directory "/var/empty")
768 (shell (file-append shadow "/sbin/nologin")))))))))
769
770 (define fcgiwrap-shepherd-service
771 (match-lambda
772 (($ <fcgiwrap-configuration> package socket user group)
773 (list (shepherd-service
774 (provision '(fcgiwrap))
775 (documentation "Run the fcgiwrap daemon.")
776 (requirement '(networking))
777 (start #~(make-forkexec-constructor
778 '(#$(file-append package "/sbin/fcgiwrap")
779 "-s" #$socket)
780 #:user #$user #:group #$group))
781 (stop #~(make-kill-destructor)))))))
782
783 (define fcgiwrap-service-type
784 (service-type (name 'fcgiwrap)
785 (extensions
786 (list (service-extension shepherd-root-service-type
787 fcgiwrap-shepherd-service)
788 (service-extension account-service-type
789 fcgiwrap-accounts)))
790 (default-value (fcgiwrap-configuration))))
791
792 (define-record-type* <php-fpm-configuration> php-fpm-configuration
793 make-php-fpm-configuration
794 php-fpm-configuration?
795 (php php-fpm-configuration-php ;<package>
796 (default php))
797 (socket php-fpm-configuration-socket
798 (default (string-append "/var/run/php"
799 (version-major (package-version php))
800 "-fpm.sock")))
801 (user php-fpm-configuration-user
802 (default "php-fpm"))
803 (group php-fpm-configuration-group
804 (default "php-fpm"))
805 (socket-user php-fpm-configuration-socket-user
806 (default "php-fpm"))
807 (socket-group php-fpm-configuration-socket-group
808 (default "nginx"))
809 (pid-file php-fpm-configuration-pid-file
810 (default (string-append "/var/run/php"
811 (version-major (package-version php))
812 "-fpm.pid")))
813 (log-file php-fpm-configuration-log-file
814 (default (string-append "/var/log/php"
815 (version-major (package-version php))
816 "-fpm.log")))
817 (process-manager php-fpm-configuration-process-manager
818 (default (php-fpm-dynamic-process-manager-configuration)))
819 (display-errors php-fpm-configuration-display-errors
820 (default #f))
821 (timezone php-fpm-configuration-timezone
822 (default #f))
823 (workers-log-file php-fpm-configuration-workers-log-file
824 (default (string-append "/var/log/php"
825 (version-major (package-version php))
826 "-fpm.www.log")))
827 (file php-fpm-configuration-file ;#f | file-like
828 (default #f)))
829
830 (define-record-type* <php-fpm-dynamic-process-manager-configuration>
831 php-fpm-dynamic-process-manager-configuration
832 make-php-fpm-dynamic-process-manager-configuration
833 php-fpm-dynamic-process-manager-configuration?
834 (max-children php-fpm-dynamic-process-manager-configuration-max-children
835 (default 5))
836 (start-servers php-fpm-dynamic-process-manager-configuration-start-servers
837 (default 2))
838 (min-spare-servers php-fpm-dynamic-process-manager-configuration-min-spare-servers
839 (default 1))
840 (max-spare-servers php-fpm-dynamic-process-manager-configuration-max-spare-servers
841 (default 3)))
842
843 (define-record-type* <php-fpm-static-process-manager-configuration>
844 php-fpm-static-process-manager-configuration
845 make-php-fpm-static-process-manager-configuration
846 php-fpm-static-process-manager-configuration?
847 (max-children php-fpm-static-process-manager-configuration-max-children
848 (default 5)))
849
850 (define-record-type* <php-fpm-on-demand-process-manager-configuration>
851 php-fpm-on-demand-process-manager-configuration
852 make-php-fpm-on-demand-process-manager-configuration
853 php-fpm-on-demand-process-manager-configuration?
854 (max-children php-fpm-on-demand-process-manager-configuration-max-children
855 (default 5))
856 (process-idle-timeout php-fpm-on-demand-process-manager-configuration-process-idle-timeout
857 (default 10)))
858
859 (define php-fpm-accounts
860 (match-lambda
861 (($ <php-fpm-configuration> php socket user group socket-user socket-group _ _ _ _ _ _)
862 (list
863 (user-group (name "php-fpm") (system? #t))
864 (user-group
865 (name group)
866 (system? #t))
867 (user-account
868 (name user)
869 (group group)
870 (supplementary-groups '("php-fpm"))
871 (system? #t)
872 (comment "php-fpm daemon user")
873 (home-directory "/var/empty")
874 (shell (file-append shadow "/sbin/nologin")))))))
875
876 (define (default-php-fpm-config socket user group socket-user socket-group
877 pid-file log-file pm display-errors timezone workers-log-file)
878 (apply mixed-text-file "php-fpm.conf"
879 (flatten
880 "[global]\n"
881 "pid =" pid-file "\n"
882 "error_log =" log-file "\n"
883 "[www]\n"
884 "user =" user "\n"
885 "group =" group "\n"
886 "listen =" socket "\n"
887 "listen.owner =" socket-user "\n"
888 "listen.group =" socket-group "\n"
889
890 (if timezone
891 (string-append "php_admin_value[date.timezone] = \"" timezone "\"\n")
892 "")
893
894 (match pm
895 (($ <php-fpm-dynamic-process-manager-configuration>
896 pm.max-children
897 pm.start-servers
898 pm.min-spare-servers
899 pm.max-spare-servers)
900 (list
901 "pm = dynamic\n"
902 "pm.max_children =" (number->string pm.max-children) "\n"
903 "pm.start_servers =" (number->string pm.start-servers) "\n"
904 "pm.min_spare_servers =" (number->string pm.min-spare-servers) "\n"
905 "pm.max_spare_servers =" (number->string pm.max-spare-servers) "\n"))
906
907 (($ <php-fpm-static-process-manager-configuration>
908 pm.max-children)
909 (list
910 "pm = static\n"
911 "pm.max_children =" (number->string pm.max-children) "\n"))
912
913 (($ <php-fpm-on-demand-process-manager-configuration>
914 pm.max-children
915 pm.process-idle-timeout)
916 (list
917 "pm = ondemand\n"
918 "pm.max_children =" (number->string pm.max-children) "\n"
919 "pm.process_idle_timeout =" (number->string pm.process-idle-timeout) "s\n")))
920
921
922 "php_flag[display_errors] = " (if display-errors "on" "off") "\n"
923
924 (if workers-log-file
925 (list "catch_workers_output = yes\n"
926 "php_admin_value[error_log] =" workers-log-file "\n"
927 "php_admin_flag[log_errors] = on\n")
928 (list "catch_workers_output = no\n")))))
929
930 (define php-fpm-shepherd-service
931 (match-lambda
932 (($ <php-fpm-configuration> php socket user group socket-user socket-group
933 pid-file log-file pm display-errors
934 timezone workers-log-file file)
935 (list (shepherd-service
936 (provision '(php-fpm))
937 (documentation "Run the php-fpm daemon.")
938 (requirement '(networking))
939 (start #~(make-forkexec-constructor
940 '(#$(file-append php "/sbin/php-fpm")
941 "--fpm-config"
942 #$(or file
943 (default-php-fpm-config socket user group
944 socket-user socket-group pid-file log-file
945 pm display-errors timezone workers-log-file)))
946 #:pid-file #$pid-file))
947 (stop #~(make-kill-destructor)))))))
948
949 (define (php-fpm-activation config)
950 #~(begin
951 (use-modules (guix build utils))
952 (let* ((user (getpwnam #$(php-fpm-configuration-user config)))
953 (touch (lambda (file-name)
954 (call-with-output-file file-name (const #t))))
955 (workers-log-file
956 #$(php-fpm-configuration-workers-log-file config))
957 (init-log-file
958 (lambda (file-name)
959 (when workers-log-file
960 (when (not (file-exists? file-name))
961 (touch file-name))
962 (chown file-name (passwd:uid user) (passwd:gid user))
963 (chmod file-name #o660)))))
964 (init-log-file #$(php-fpm-configuration-log-file config))
965 (init-log-file workers-log-file))))
966
967
968 (define php-fpm-service-type
969 (service-type
970 (name 'php-fpm)
971 (description
972 "Run @command{php-fpm} to provide a fastcgi socket for calling php through
973 a webserver.")
974 (extensions
975 (list (service-extension shepherd-root-service-type
976 php-fpm-shepherd-service)
977 (service-extension activation-service-type
978 php-fpm-activation)
979 (service-extension account-service-type
980 php-fpm-accounts)))
981 (default-value (php-fpm-configuration))))
982
983 (define* (nginx-php-location
984 #:key
985 (nginx-package nginx)
986 (socket (string-append "/var/run/php"
987 (version-major (package-version php))
988 "-fpm.sock")))
989 "Return a nginx-location-configuration that makes nginx run .php files."
990 (nginx-location-configuration
991 (uri "~ \\.php$")
992 (body (list
993 "fastcgi_split_path_info ^(.+\\.php)(/.+)$;"
994 (string-append "fastcgi_pass unix:" socket ";")
995 "fastcgi_index index.php;"
996 (list "include " nginx-package "/share/nginx/conf/fastcgi.conf;")))))
997
998 (define* (cat-avatar-generator-service
999 #:key
1000 (cache-dir "/var/cache/cat-avatar-generator")
1001 (package cat-avatar-generator)
1002 (configuration (nginx-server-configuration)))
1003 (simple-service
1004 'cat-http-server nginx-service-type
1005 (list (nginx-server-configuration
1006 (inherit configuration)
1007 (locations
1008 (cons
1009 (let ((base (nginx-php-location)))
1010 (nginx-location-configuration
1011 (inherit base)
1012 (body (list (string-append "fastcgi_param CACHE_DIR \""
1013 cache-dir "\";")
1014 (nginx-location-configuration-body base)))))
1015 (nginx-server-configuration-locations configuration)))
1016 (root #~(string-append #$package
1017 "/share/web/cat-avatar-generator"))))))
1018
1019 \f
1020 (define-record-type* <hpcguix-web-configuration>
1021 hpcguix-web-configuration make-hpcguix-web-configuration
1022 hpcguix-web-configuration?
1023
1024 (package hpcguix-web-package (default hpcguix-web)) ;<package>
1025
1026 ;; Specs is gexp of hpcguix-web configuration file
1027 (specs hpcguix-web-configuration-specs))
1028
1029 (define %hpcguix-web-accounts
1030 (list (user-group
1031 (name "hpcguix-web")
1032 (system? #t))
1033 (user-account
1034 (name "hpcguix-web")
1035 (group "hpcguix-web")
1036 (system? #t)
1037 (comment "hpcguix-web")
1038 (home-directory "/var/empty")
1039 (shell (file-append shadow "/sbin/nologin")))))
1040
1041 (define %hpcguix-web-activation
1042 #~(begin
1043 (use-modules (guix build utils))
1044 (let ((home-dir "/var/cache/guix/web")
1045 (user (getpwnam "hpcguix-web")))
1046 (mkdir-p home-dir)
1047 (chown home-dir (passwd:uid user) (passwd:gid user))
1048 (chmod home-dir #o755))))
1049
1050 (define %hpcguix-web-log-file
1051 "/var/log/hpcguix-web.log")
1052
1053 (define %hpcguix-web-log-rotations
1054 (list (log-rotation
1055 (files (list %hpcguix-web-log-file))
1056 (frequency 'weekly))))
1057
1058 (define (hpcguix-web-shepherd-service config)
1059 (let ((specs (hpcguix-web-configuration-specs config))
1060 (hpcguix-web (hpcguix-web-package config)))
1061 (with-imported-modules (source-module-closure
1062 '((gnu build shepherd)))
1063 (shepherd-service
1064 (documentation "hpcguix-web daemon")
1065 (provision '(hpcguix-web))
1066 (requirement '(networking))
1067 (start #~(make-forkexec-constructor
1068 (list #$(file-append hpcguix-web "/bin/run")
1069 (string-append "--config="
1070 #$(scheme-file "hpcguix-web.scm" specs)))
1071 #:user "hpcguix-web"
1072 #:group "hpcguix-web"
1073 #:environment-variables
1074 (list "XDG_CACHE_HOME=/var/cache"
1075 "SSL_CERT_DIR=/etc/ssl/certs")
1076 #:log-file #$%hpcguix-web-log-file))
1077 (stop #~(make-kill-destructor))))))
1078
1079 (define hpcguix-web-service-type
1080 (service-type
1081 (name 'hpcguix-web)
1082 (description "Run the hpcguix-web server.")
1083 (extensions
1084 (list (service-extension account-service-type
1085 (const %hpcguix-web-accounts))
1086 (service-extension activation-service-type
1087 (const %hpcguix-web-activation))
1088 (service-extension rottlog-service-type
1089 (const %hpcguix-web-log-rotations))
1090 (service-extension shepherd-root-service-type
1091 (compose list hpcguix-web-shepherd-service))))))
1092
1093 \f
1094 ;;;
1095 ;;; Tailon
1096 ;;;
1097
1098 (define-record-type* <tailon-configuration-file>
1099 tailon-configuration-file make-tailon-configuration-file
1100 tailon-configuration-file?
1101 (files tailon-configuration-file-files
1102 (default '("/var/log")))
1103 (bind tailon-configuration-file-bind
1104 (default "localhost:8080"))
1105 (relative-root tailon-configuration-file-relative-root
1106 (default #f))
1107 (allow-transfers? tailon-configuration-file-allow-transfers?
1108 (default #t))
1109 (follow-names? tailon-configuration-file-follow-names?
1110 (default #t))
1111 (tail-lines tailon-configuration-file-tail-lines
1112 (default 200))
1113 (allowed-commands tailon-configuration-file-allowed-commands
1114 (default '("tail" "grep" "awk")))
1115 (debug? tailon-configuration-file-debug?
1116 (default #f))
1117 (wrap-lines tailon-configuration-file-wrap-lines
1118 (default #t))
1119 (http-auth tailon-configuration-file-http-auth
1120 (default #f))
1121 (users tailon-configuration-file-users
1122 (default #f)))
1123
1124 (define (tailon-configuration-files-string files)
1125 (string-append
1126 "\n"
1127 (string-join
1128 (map
1129 (lambda (x)
1130 (string-append
1131 " - "
1132 (cond
1133 ((string? x)
1134 (simple-format #f "'~A'" x))
1135 ((list? x)
1136 (string-join
1137 (cons (simple-format #f "'~A':" (car x))
1138 (map
1139 (lambda (x) (simple-format #f " - '~A'" x))
1140 (cdr x)))
1141 "\n"))
1142 (else (error x)))))
1143 files)
1144 "\n")))
1145
1146 (define-gexp-compiler (tailon-configuration-file-compiler
1147 (file <tailon-configuration-file>) system target)
1148 (match file
1149 (($ <tailon-configuration-file> files bind relative-root
1150 allow-transfers? follow-names?
1151 tail-lines allowed-commands debug?
1152 wrap-lines http-auth users)
1153 (text-file
1154 "tailon-config.yaml"
1155 (string-concatenate
1156 (filter-map
1157 (match-lambda
1158 ((key . #f) #f)
1159 ((key . value) (string-append key ": " value "\n")))
1160
1161 `(("files" . ,(tailon-configuration-files-string files))
1162 ("bind" . ,bind)
1163 ("relative-root" . ,relative-root)
1164 ("allow-transfers" . ,(if allow-transfers? "true" "false"))
1165 ("follow-names" . ,(if follow-names? "true" "false"))
1166 ("tail-lines" . ,(number->string tail-lines))
1167 ("commands" . ,(string-append "["
1168 (string-join allowed-commands ", ")
1169 "]"))
1170 ("debug" . ,(if debug? "true" #f))
1171 ("wrap-lines" . ,(if wrap-lines "true" "false"))
1172 ("http-auth" . ,http-auth)
1173 ("users" . ,(if users
1174 (string-concatenate
1175 (cons "\n"
1176 (map (match-lambda
1177 ((user . pass)
1178 (string-append
1179 " " user ":" pass)))
1180 users)))
1181 #f)))))))))
1182
1183 (define-record-type* <tailon-configuration>
1184 tailon-configuration make-tailon-configuration
1185 tailon-configuration?
1186 (config-file tailon-configuration-config-file
1187 (default (tailon-configuration-file)))
1188 (package tailon-configuration-package
1189 (default tailon)))
1190
1191 (define tailon-shepherd-service
1192 (match-lambda
1193 (($ <tailon-configuration> config-file package)
1194 (list (shepherd-service
1195 (provision '(tailon))
1196 (documentation "Run the tailon daemon.")
1197 (start #~(make-forkexec-constructor
1198 `(,(string-append #$package "/bin/tailon")
1199 "-c" ,#$config-file)
1200 #:user "tailon"
1201 #:group "tailon"))
1202 (stop #~(make-kill-destructor)))))))
1203
1204 (define %tailon-accounts
1205 (list (user-group (name "tailon") (system? #t))
1206 (user-account
1207 (name "tailon")
1208 (group "tailon")
1209 (system? #t)
1210 (comment "tailon")
1211 (home-directory "/var/empty")
1212 (shell (file-append shadow "/sbin/nologin")))))
1213
1214 (define tailon-service-type
1215 (service-type
1216 (name 'tailon)
1217 (description
1218 "Run Tailon, a Web application for monitoring, viewing, and searching log
1219 files.")
1220 (extensions
1221 (list (service-extension shepherd-root-service-type
1222 tailon-shepherd-service)
1223 (service-extension account-service-type
1224 (const %tailon-accounts))))
1225 (compose concatenate)
1226 (extend (lambda (parameter files)
1227 (tailon-configuration
1228 (inherit parameter)
1229 (config-file
1230 (let ((old-config-file
1231 (tailon-configuration-config-file parameter)))
1232 (tailon-configuration-file
1233 (inherit old-config-file)
1234 (files (append (tailon-configuration-file-files old-config-file)
1235 files))))))))
1236 (default-value (tailon-configuration))))
1237
1238 \f
1239 ;;;
1240 ;;; Varnish
1241 ;;;
1242
1243 (define-record-type* <varnish-configuration>
1244 varnish-configuration make-varnish-configuration
1245 varnish-configuration?
1246 (package varnish-configuration-package ;<package>
1247 (default varnish))
1248 (name varnish-configuration-name ;string
1249 (default "default"))
1250 (backend varnish-configuration-backend ;string
1251 (default "localhost:8080"))
1252 (vcl varnish-configuration-vcl ;#f | <file-like>
1253 (default #f))
1254 (listen varnish-configuration-listen ;list of strings
1255 (default '("localhost:80")))
1256 (storage varnish-configuration-storage ;list of strings
1257 (default '("malloc,128m")))
1258 (parameters varnish-configuration-parameters ;list of string pairs
1259 (default '()))
1260 (extra-options varnish-configuration-extra-options ;list of strings
1261 (default '())))
1262
1263 (define %varnish-accounts
1264 (list (user-group
1265 (name "varnish")
1266 (system? #t))
1267 (user-account
1268 (name "varnish")
1269 (group "varnish")
1270 (system? #t)
1271 (comment "Varnish Cache User")
1272 (home-directory "/var/varnish")
1273 (shell (file-append shadow "/sbin/nologin")))))
1274
1275 (define varnish-shepherd-service
1276 (match-lambda
1277 (($ <varnish-configuration> package name backend vcl listen storage
1278 parameters extra-options)
1279 (list (shepherd-service
1280 (provision (list (symbol-append 'varnish- (string->symbol name))))
1281 (documentation (string-append "The Varnish Web Accelerator"
1282 " (" name ")"))
1283 (requirement '(networking))
1284 (start #~(make-forkexec-constructor
1285 (list #$(file-append package "/sbin/varnishd")
1286 "-n" #$name
1287 #$@(if vcl
1288 #~("-f" #$vcl)
1289 #~("-b" #$backend))
1290 #$@(append-map (lambda (a) (list "-a" a)) listen)
1291 #$@(append-map (lambda (s) (list "-s" s)) storage)
1292 #$@(append-map (lambda (p)
1293 (list "-p" (format #f "~a=~a"
1294 (car p) (cdr p))))
1295 parameters)
1296 #$@extra-options)
1297 ;; Varnish will drop privileges to the "varnish" user when
1298 ;; it exists. Not passing #:user here allows the service
1299 ;; to bind to ports < 1024.
1300 #:pid-file (if (string-prefix? "/" #$name)
1301 (string-append #$name "/_.pid")
1302 (string-append "/var/varnish/" #$name "/_.pid"))))
1303 (stop #~(make-kill-destructor)))))))
1304
1305 (define varnish-service-type
1306 (service-type
1307 (name 'varnish)
1308 (description "Run the Varnish cache server.")
1309 (extensions
1310 (list (service-extension account-service-type
1311 (const %varnish-accounts))
1312 (service-extension shepherd-root-service-type
1313 varnish-shepherd-service)))
1314 (default-value
1315 (varnish-configuration))))
1316
1317 \f
1318 ;;;
1319 ;;; Patchwork
1320 ;;;
1321
1322 (define-record-type* <patchwork-database-configuration>
1323 patchwork-database-configuration make-patchwork-database-configuration
1324 patchwork-database-configuration?
1325 (engine patchwork-database-configuration-engine
1326 (default "django.db.backends.postgresql_psycopg2"))
1327 (name patchwork-database-configuration-name
1328 (default "patchwork"))
1329 (user patchwork-database-configuration-user
1330 (default "httpd"))
1331 (password patchwork-database-configuration-password
1332 (default ""))
1333 (host patchwork-database-configuration-host
1334 (default ""))
1335 (port patchwork-database-configuration-port
1336 (default "")))
1337
1338 (define-record-type* <patchwork-settings-module>
1339 patchwork-settings-module make-patchwork-settings-module
1340 patchwork-settings-module?
1341 (database-configuration patchwork-settings-module-database-configuration
1342 (default (patchwork-database-configuration)))
1343 (secret-key-file patchwork-settings-module-secret-key-file
1344 (default "/etc/patchwork/django-secret-key"))
1345 (allowed-hosts patchwork-settings-module-allowed-hosts)
1346 (default-from-email patchwork-settings-module-default-from-email)
1347 (static-url patchwork-settings-module-static-url
1348 (default "/static/"))
1349 (admins patchwork-settings-module-admins
1350 (default '()))
1351 (debug? patchwork-settings-module-debug?
1352 (default #f))
1353 (enable-rest-api? patchwork-settings-module-enable-rest-api?
1354 (default #t))
1355 (enable-xmlrpc? patchwork-settings-module-enable-xmlrpc?
1356 (default #t))
1357 (force-https-links? patchwork-settings-module-force-https-links?
1358 (default #t))
1359 (extra-settings patchwork-settings-module-extra-settings
1360 (default "")))
1361
1362 (define-record-type* <patchwork-configuration>
1363 patchwork-configuration make-patchwork-configuration
1364 patchwork-configuration?
1365 (patchwork patchwork-configuration-patchwork
1366 (default patchwork))
1367 (domain patchwork-configuration-domain)
1368 (settings-module patchwork-configuration-settings-module)
1369 (static-path patchwork-configuration-static-url
1370 (default "/static/"))
1371 (getmail-retriever-config getmail-retriever-config))
1372
1373 ;; Django uses a Python module for configuration, so this compiler generates a
1374 ;; Python module from the configuration record.
1375 (define-gexp-compiler (patchwork-settings-module-compiler
1376 (file <patchwork-settings-module>) system target)
1377 (match file
1378 (($ <patchwork-settings-module> database-configuration secret-key-file
1379 allowed-hosts default-from-email
1380 static-url admins debug? enable-rest-api?
1381 enable-xmlrpc? force-https-links?
1382 extra-configuration)
1383 (gexp->derivation
1384 "patchwork-settings"
1385 (with-imported-modules '((guix build utils))
1386 #~(let ((output #$output))
1387 (define (create-__init__.py filename)
1388 (call-with-output-file filename
1389 (lambda (port) (display "" port))))
1390
1391 (use-modules (guix build utils)
1392 (srfi srfi-1))
1393
1394 (mkdir-p (string-append output "/guix/patchwork"))
1395 (create-__init__.py
1396 (string-append output "/guix/__init__.py"))
1397 (create-__init__.py
1398 (string-append output "/guix/patchwork/__init__.py"))
1399
1400 (call-with-output-file
1401 (string-append output "/guix/patchwork/settings.py")
1402 (lambda (port)
1403 (display
1404 (string-append "from patchwork.settings.base import *
1405
1406 # Configuration from Guix
1407 with open('" #$secret-key-file "') as f:
1408 SECRET_KEY = f.read().strip()
1409
1410 ALLOWED_HOSTS = [
1411 " #$(string-concatenate
1412 (map (lambda (allowed-host)
1413 (string-append " '" allowed-host "'\n"))
1414 allowed-hosts))
1415 "]
1416
1417 ADMINS = [
1418 " #$(string-concatenate
1419 (map (match-lambda
1420 ((name email-address)
1421 (string-append
1422 "('" name "','" email-address "'),")))
1423 admins))
1424 "]
1425
1426 DEBUG = " #$(if debug? "True" "False") "
1427
1428 ENABLE_REST_API = " #$(if enable-xmlrpc? "True" "False") "
1429 ENABLE_XMLRPC = " #$(if enable-xmlrpc? "True" "False") "
1430
1431 FORCE_HTTPS_LINKS = " #$(if force-https-links? "True" "False") "
1432
1433 DATABASES = {
1434 'default': {
1435 " #$(match database-configuration
1436 (($ <patchwork-database-configuration>
1437 engine name user password host port)
1438 (string-append
1439 " 'ENGINE': '" engine "',\n"
1440 " 'NAME': '" name "',\n"
1441 " 'USER': '" user "',\n"
1442 " 'PASSWORD': '" password "',\n"
1443 " 'HOST': '" host "',\n"
1444 " 'PORT': '" port "',\n"))) "
1445 },
1446 }
1447
1448 " #$(if debug?
1449 #~(string-append "STATIC_ROOT = '"
1450 #$(file-append patchwork "/share/patchwork/htdocs")
1451 "'")
1452 #~(string-append "STATIC_URL = '" #$static-url "'")) "
1453
1454 STATICFILES_STORAGE = (
1455 'django.contrib.staticfiles.storage.StaticFilesStorage'
1456 )
1457
1458 # Guix Extra Configuration
1459 " #$extra-configuration "
1460 ") port)))
1461 #t))
1462 #:local-build? #t))))
1463
1464 (define patchwork-virtualhost
1465 (match-lambda
1466 (($ <patchwork-configuration> patchwork domain
1467 settings-module static-path
1468 getmail-retriever-config)
1469 (define wsgi.py
1470 (file-append patchwork
1471 (string-append
1472 "/lib/python"
1473 (version-major+minor
1474 (package-version python))
1475 "/site-packages/patchwork/wsgi.py")))
1476
1477 (httpd-virtualhost
1478 "*:8080"
1479 `("ServerAdmin admin@example.com`
1480 ServerName " ,domain "
1481
1482 LogFormat \"%v %h %l %u %t \\\"%r\\\" %>s %b \\\"%{Referer}i\\\" \\\"%{User-Agent}i\\\"\" customformat
1483 LogLevel info
1484 CustomLog \"/var/log/httpd/" ,domain "-access_log\" customformat
1485
1486 ErrorLog /var/log/httpd/error.log
1487
1488 WSGIScriptAlias / " ,wsgi.py "
1489 WSGIDaemonProcess " ,(package-name patchwork) " user=httpd group=httpd processes=1 threads=2 display-name=%{GROUP} lang='en_US.UTF-8' locale='en_US.UTF-8' python-path=" ,settings-module "
1490 WSGIProcessGroup " ,(package-name patchwork) "
1491 WSGIPassAuthorization On
1492
1493 <Files " ,wsgi.py ">
1494 Require all granted
1495 </Files>
1496
1497 " ,@(if static-path
1498 `("Alias " ,static-path " " ,patchwork "/share/patchwork/htdocs/")
1499 '())
1500 "
1501 <Directory \"/srv/http/" ,domain "/\">
1502 AllowOverride None
1503 Options MultiViews Indexes SymlinksIfOwnerMatch IncludesNoExec
1504 Require method GET POST OPTIONS
1505 </Directory>")))))
1506
1507 (define (patchwork-httpd-configuration patchwork-configuration)
1508 (list "WSGISocketPrefix /var/run/mod_wsgi"
1509 (list "LoadModule wsgi_module "
1510 (file-append mod-wsgi "/modules/mod_wsgi.so"))
1511 (patchwork-virtualhost patchwork-configuration)))
1512
1513 (define (patchwork-django-admin-gexp patchwork settings-module)
1514 #~(lambda command
1515 (let ((pid (primitive-fork))
1516 (user (getpwnam "httpd")))
1517 (if (eq? pid 0)
1518 (dynamic-wind
1519 (const #t)
1520 (lambda ()
1521 (setgid (passwd:gid user))
1522 (setuid (passwd:uid user))
1523
1524 (setenv "DJANGO_SETTINGS_MODULE" "guix.patchwork.settings")
1525 (setenv "PYTHONPATH" #$settings-module)
1526 (primitive-exit
1527 (if (zero?
1528 (apply system*
1529 #$(file-append patchwork "/bin/patchwork-admin")
1530 command))
1531 0
1532 1)))
1533 (lambda ()
1534 (primitive-exit 1)))
1535 (zero? (cdr (waitpid pid)))))))
1536
1537 (define (patchwork-django-admin-action patchwork settings-module)
1538 (shepherd-action
1539 (name 'django-admin)
1540 (documentation
1541 "Run a django admin command for patchwork")
1542 (procedure (patchwork-django-admin-gexp patchwork settings-module))))
1543
1544 (define patchwork-shepherd-services
1545 (match-lambda
1546 (($ <patchwork-configuration> patchwork domain
1547 settings-module static-path
1548 getmail-retriever-config)
1549 (define secret-key-file-creation-gexp
1550 (if (patchwork-settings-module? settings-module)
1551 (with-extensions (list guile-gcrypt)
1552 #~(let ((secret-key-file
1553 #$(patchwork-settings-module-secret-key-file
1554 settings-module)))
1555 (use-modules (guix build utils)
1556 (gcrypt random))
1557
1558 (unless (file-exists? secret-key-file)
1559 (mkdir-p (dirname secret-key-file))
1560 (call-with-output-file secret-key-file
1561 (lambda (port)
1562 (display (random-token 30 'very-strong) port)))
1563 (let* ((pw (getpwnam "httpd"))
1564 (uid (passwd:uid pw))
1565 (gid (passwd:gid pw)))
1566 (chown secret-key-file uid gid)
1567 (chmod secret-key-file #o400)))))
1568 #~()))
1569
1570 (list (shepherd-service
1571 (requirement '(postgres))
1572 (provision (list (string->symbol
1573 (string-append (package-name patchwork)
1574 "-setup"))))
1575 (start
1576 #~(lambda ()
1577 (define run-django-admin-command
1578 #$(patchwork-django-admin-gexp patchwork
1579 settings-module))
1580
1581 #$secret-key-file-creation-gexp
1582
1583 (run-django-admin-command "migrate")))
1584 (stop #~(const #f))
1585 (actions
1586 (list (patchwork-django-admin-action patchwork
1587 settings-module)))
1588 (respawn? #f)
1589 (documentation "Setup Patchwork."))))))
1590
1591 (define patchwork-getmail-configs
1592 (match-lambda
1593 (($ <patchwork-configuration> patchwork domain
1594 settings-module static-path
1595 getmail-retriever-config)
1596 (list
1597 (getmail-configuration
1598 (name (string->symbol (package-name patchwork)))
1599 (user "httpd")
1600 (directory (string-append
1601 "/var/lib/getmail/" (package-name patchwork)))
1602 (rcfile
1603 (getmail-configuration-file
1604 (retriever getmail-retriever-config)
1605 (destination
1606 (getmail-destination-configuration
1607 (type "MDA_external")
1608 (path (file-append patchwork "/bin/patchwork-admin"))
1609 (extra-parameters
1610 '((arguments . ("parsemail"))))))
1611 (options
1612 (getmail-options-configuration
1613 (read-all #f)
1614 (delivered-to #f)
1615 (received #f)))))
1616 (idle (assq-ref
1617 (getmail-retriever-configuration-extra-parameters
1618 getmail-retriever-config)
1619 'mailboxes))
1620 (environment-variables
1621 (list "DJANGO_SETTINGS_MODULE=guix.patchwork.settings"
1622 #~(string-append "PYTHONPATH=" #$settings-module))))))))
1623
1624 (define patchwork-service-type
1625 (service-type
1626 (name 'patchwork-setup)
1627 (extensions
1628 (list (service-extension httpd-service-type
1629 patchwork-httpd-configuration)
1630 (service-extension shepherd-root-service-type
1631 patchwork-shepherd-services)
1632 (service-extension getmail-service-type
1633 patchwork-getmail-configs)))
1634 (description
1635 "Patchwork patch tracking system.")))