From 32afafc93077e9d6d57354540967dfa1a42cb9b1 Mon Sep 17 00:00:00 2001 From: Arun Isaac Date: Wed, 3 Jan 2024 00:41:40 +0000 Subject: Add GeneNetwork service. * genenetwork/services/genenetwork.scm: New file. --- genenetwork/services/genenetwork.scm | 266 +++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 genenetwork/services/genenetwork.scm (limited to 'genenetwork/services') 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 +;;; +;;; 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 +;;; . + +(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 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 + (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{} +object." + (match-record config + (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{} record specifying +reverse proxy of the genenetwork service described by @var{config}, a +@code{} record." + (match-record config + (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)))) -- cgit v1.2.3