about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--genenetwork/services/genenetwork.scm266
1 files changed, 266 insertions, 0 deletions
diff --git a/genenetwork/services/genenetwork.scm b/genenetwork/services/genenetwork.scm
new file mode 100644
index 0000000..966592d
--- /dev/null
+++ b/genenetwork/services/genenetwork.scm
@@ -0,0 +1,266 @@
+;;; genenetwork-machines --- Guix configuration for genenetwork machines
+;;; Copyright © 2024 Arun Isaac <arunisaac@systemreboot.net>
+;;;
+;;; This file is part of genenetwork-machines.
+;;;
+;;; genenetwork-machines is free software: you can redistribute it
+;;; and/or modify it under the terms of the GNU General Public License
+;;; as published by the Free Software Foundation, either version 3 of
+;;; the License, or (at your option) any later version.
+;;;
+;;; genenetwork-machines is distributed in the hope that it will be
+;;; useful, but WITHOUT ANY WARRANTY; without even the implied
+;;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+;;; See the GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with genenetwork-machines.  If not, see
+;;; <https://www.gnu.org/licenses/>.
+
+(define-module (genenetwork services genenetwork)
+  #:use-module ((gn packages genenetwork) #:select (genenetwork2 genenetwork3))
+  #:use-module ((gnu packages admin) #:select (shadow))
+  #:use-module (gnu services)
+  #:use-module (gnu services web)
+  #:use-module (gnu system file-systems)
+  #:use-module (gnu system shadow)
+  #:use-module (guix gexp)
+  #:use-module (guix profiles)
+  #:use-module (guix records)
+  #:use-module (forge environment)
+  #:use-module (forge nginx)
+  #:use-module (forge gunicorn)
+  #:use-module (forge socket)
+  #:use-module (srfi srfi-1)
+  #:use-module (ice-9 match)
+  #:export (genenetwork-service-type
+            genenetwork-configuration
+            genenetwork-configuration?
+            genenetwork-configuration-genenetwork2
+            genenetwork-configuration-genenetwork3
+            genenetwork-configuration-server-name
+            genenetwork-configuration-port
+            genenetwork-configuration-gn2-port
+            genenetwork-configuration-gn3-port
+            genenetwork-configuration-auth-db
+            genenetwork-configuration-xapian-db
+            genenetwork-configuration-genotype-files
+            genenetwork-configuration-sparql-endpoint
+            genenetwork-configuration-gn3-data-directory
+            genenetwork-configuration-gn2-secrets
+            genenetwork-configuration-gn3-secrets))
+
+(define-record-type* <genenetwork-configuration>
+  genenetwork-configuration make-genenetwork-configuration
+  genenetwork-configuration?
+  (genenetwork2 genenetwork-configuration-genenetwork2
+                (default genenetwork2))
+  (genenetwork3 genenetwork-configuration-genenetwork3
+                (default genenetwork3))
+  (server-name genenetwork-configuration-server-name
+               (default "genenetwork.org"))
+  (gn2-port genenetwork-configuration-gn2-port
+            (default 8082))
+  (gn3-port genenetwork-configuration-gn3-port
+            (default 8083))
+  (sql-uri genenetwork-configuration-sql-uri
+           (default "mysql://username:password@localhost/database"))
+  (auth-db genenetwork-configuration-auth-db
+           (default "/var/genenetwork/auth.db"))
+  (xapian-db genenetwork-configuration-xapian-db
+             (default "/var/genenetwork/xapian"))
+  (genotype-files genenetwork-configuration-genotype-files
+                  (default "/var/genenetwork/genotype-files"))
+  (sparql-endpoint genenetwork-configuration-sparql-endpoint
+                   (default "http://localhost:8081/sparql"))
+  (gn3-data-directory genenetwork-configuration-gn3-data-directory
+                      (default "/var/genenetwork"))
+  (gn2-secrets genenetwork-configuration-gn2-secrets
+               (default "/etc/genenetwork/gn2-secrets.py"))
+  (gn3-secrets genenetwork-configuration-gn3-secrets
+               (default "/etc/genenetwork/gn3-secrets.py")))
+
+(define %genenetwork-accounts
+  (list (user-group
+         (name "genenetwork")
+         (system? #t))
+        (user-account
+         (name "genenetwork")
+         (group "genenetwork")
+         (system? #t)
+         (comment "GeneNetwork user")
+         (home-directory "/var/empty")
+         (shell (file-append shadow "/sbin/nologin")))))
+
+(define (genenetwork-activation config)
+  (match-record config <genenetwork-configuration>
+    (auth-db)
+    (with-imported-modules '((guix build utils))
+      #~(begin
+          (use-modules (guix build utils))
+
+          (for-each (lambda (file)
+                      (chown file
+                             (passwd:uid (getpw "genenetwork"))
+                             (passwd:gid (getpw "genenetwork"))))
+                    (find-files #$(dirname auth-db)
+                                #:directories? #t))))))
+
+(define (configuration-file-gexp alist)
+  "Return a G-expression that constructs a configuration file of
+key-value pairs. @var{alist} is an association list mapping keys to
+their values. Keys must be strings. Values may be strings,
+G-expressions or numbers."
+  #~(begin
+      (use-modules (ice-9 match))
+      
+      (call-with-output-file #$output
+        (lambda (port)
+          (for-each (match-lambda
+                      ((key value)
+                       (display key port)
+                       (display " = " port)
+                       (cond
+                        ((number? value)
+                         (display value port))
+                        (else
+                         (display "\"" port)
+                         (display value port)
+                         (display "\"" port)))
+                       (newline port)))
+                    '#$alist)))))
+
+(define (genenetwork-gunicorn-apps config)
+  "Return a list of gunicorn apps to run the genenetwork server
+described by @var{config}, a @code{<genenetwork-configuration>}
+object."
+  (match-record config <genenetwork-configuration>
+    (genenetwork2 genenetwork3 server-name gn2-port gn3-port sql-uri auth-db xapian-db genotype-files sparql-endpoint gn3-data-directory gn2-secrets gn3-secrets)
+    ;; If we mapped only the mysqld.sock socket file, it would break
+    ;; when the external mysqld server is restarted.
+    (let* ((database-mapping (file-system-mapping
+                              (source "/run/mysqld")
+                              (target source)
+                              (writable? #t)))
+           (gn2-profile (profile
+                         (content (package->development-manifest genenetwork2))
+                         (allow-collisions? #t)))
+           (gn2-conf (computed-file "gn2.conf"
+                                    (configuration-file-gexp
+                                     `(("GEMMA_COMMAND" ,(file-append gn2-profile "/bin/gemma"))
+                                       ("GEMMA_WRAPPER_COMMAND" ,(file-append gn2-profile "/bin/gemma-wrapper"))
+                                       ("GENENETWORK_FILES" ,genotype-files)
+                                       ("GN2_SECRETS" ,gn2-secrets)
+                                       ("GN3_LOCAL_URL" ,(string-append "http://localhost:"
+                                                                        (number->string gn3-port)))
+                                       ("GN_SERVER_URL" ,(string-append "https://" server-name "/api3/"))
+                                       ("JS_GUIX_PATH" ,(file-append gn2-profile "/share/genenetwork2/javascript"))
+                                       ("PLINK_COMMAND" ,(file-append gn2-profile "/bin/plink2"))
+                                       ("SQL_URI" ,sql-uri)))))
+           (gn3-conf (computed-file "gn3.conf"
+                                    (configuration-file-gexp
+                                     `(("AUTH_DB" ,auth-db)
+                                       ("DATA_DIR" ,gn3-data-directory)
+                                       ("SPARQL_ENDPOINT" ,sparql-endpoint)
+                                       ("SQL_URI" ,sql-uri)
+                                       ("XAPIAN_DB_PATH" ,xapian-db))))))
+      (list (gunicorn-app
+             (name "genenetwork2")
+             (package genenetwork2)
+             (sockets (list (forge-ip-socket
+                             (port gn2-port))))
+             (wsgi-app-module "gn2.wsgi")
+             (workers 20)
+             (environment-variables
+              (list (environment-variable
+                     (name "GN2_PROFILE")
+                     (value gn2-profile))
+                    (environment-variable
+                     (name "GN2_SETTINGS")
+                     (value gn2-conf))
+                    (environment-variable
+                     (name "HOME")
+                     (value "/tmp"))))
+             (mappings (list database-mapping
+                             (file-system-mapping
+                              (source genotype-files)
+                              (target source))
+                             (file-system-mapping
+                              (source gn2-conf)
+                              (target source))
+                             (file-system-mapping
+                              (source gn2-profile)
+                              (target source))
+                             (file-system-mapping
+                              (source gn2-secrets)
+                              (target source)))))
+            (gunicorn-app
+             (name "genenetwork3")
+             (package genenetwork3)
+             (sockets (list (forge-ip-socket
+                             (port gn3-port))))
+             (wsgi-app-module "gn3.app:create_app()")
+             (workers 20)
+             (environment-variables
+              (list (environment-variable
+                     (name "GN3_CONF")
+                     (value gn3-conf))
+                    (environment-variable
+                     (name "GN3_SECRETS")
+                     (value gn3-secrets))
+                    (environment-variable
+                     (name "HOME")
+                     (value "/tmp"))))
+             (mappings (list database-mapping
+                             (file-system-mapping
+                              (source gn3-conf)
+                              (target source))
+                             (file-system-mapping
+                              (source gn3-secrets)
+                              (target source))
+                             (file-system-mapping
+                              (source gn3-data-directory)
+                              (target source))
+                             (file-system-mapping
+                              (source xapian-db)
+                              (target source))
+                             (file-system-mapping
+                              (source auth-db)
+                              (target source)
+                              (writable? #t)))))))))
+
+(define (genenetwork-nginx-server-block config)
+  "Return an @code{<nginx-server-configuration>} record specifying
+reverse proxy of the genenetwork service described by @var{config}, a
+@code{<genenetwork-configuration>} record."
+  (match-record config <genenetwork-configuration>
+    (server-name gn2-port gn3-port)
+    (nginx-server-configuration
+     (server-name (list server-name))
+     (locations
+      (list (nginx-location-configuration
+             (uri "/")
+             (body (list (string-append "proxy_pass http://localhost:"
+                                        (number->string gn2-port) ";")
+                         "proxy_set_header Host $host;")))
+            (nginx-location-configuration
+             (uri "/api3/")
+             (body (list "rewrite /api3/(.*) /api/$1 break;"
+                         (string-append "proxy_pass http://localhost:"
+                                        (number->string gn3-port) ";")
+                         "proxy_set_header Host $host;"))))))))
+
+(define genenetwork-service-type
+  (service-type
+   (name 'genenetwork)
+   (description "Run GeneNetwork")
+   (extensions
+    (list (service-extension account-service-type
+                             (const %genenetwork-accounts))
+          (service-extension activation-service-type
+                             genenetwork-activation)
+          (service-extension gunicorn-service-type
+                             genenetwork-gunicorn-apps)
+          (service-extension forge-nginx-service-type
+                             (compose list genenetwork-nginx-server-block))))
+   (default-value (genenetwork-configuration))))