aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xetc/auth.dbbin0 -> 14548992 bytes
-rw-r--r--etc/conn.scm11
-rw-r--r--etc/gn-auth-secrets.py8
-rw-r--r--etc/gn-uploader-secrets.py3
-rw-r--r--etc/gn-uploader.db0
-rw-r--r--etc/gn2-secrets.py3
-rw-r--r--etc/gn3-secrets.py7
-rw-r--r--etc/llm.db0
-rw-r--r--genenetwork-development.scm18
-rw-r--r--genenetwork-local-container.scm662
-rwxr-xr-xgenenetwork-local-container.sh650
-rw-r--r--genenetwork/services/genenetwork.scm149
-rwxr-xr-xproduction-deploy.sh6
-rw-r--r--production.scm10
-rwxr-xr-xpublic-sparql-deploy.sh1
-rw-r--r--public-sparql.scm7
-rwxr-xr-xuploader-deploy.sh26
-rw-r--r--uploader.scm24
-rwxr-xr-xvirtuoso-deploy.sh1
-rw-r--r--virtuoso.scm2
20 files changed, 1505 insertions, 83 deletions
diff --git a/etc/auth.db b/etc/auth.db
new file mode 100755
index 0000000..ca05c2d
--- /dev/null
+++ b/etc/auth.db
Binary files differ
diff --git a/etc/conn.scm b/etc/conn.scm
new file mode 100644
index 0000000..6f448ab
--- /dev/null
+++ b/etc/conn.scm
@@ -0,0 +1,11 @@
+((sql-username . "webqtlout")
+ (sql-password . "webqtlout")
+ (sql-database . "db_webqtl_local")
+ (sql-host . "localhost")
+ (sql-port . 3306)
+ (virtuoso-port . 7081)
+ (virtuoso-username . "dba")
+ (virtuoso-password . "")
+ (sparql-scheme . http)
+ (sparql-host . "localhost")
+ (sparql-port . 7082))
diff --git a/etc/gn-auth-secrets.py b/etc/gn-auth-secrets.py
new file mode 100644
index 0000000..0478bcf
--- /dev/null
+++ b/etc/gn-auth-secrets.py
@@ -0,0 +1,8 @@
+SECRET_KEY = "qQIrgiK29kXZU6v8D09y4uw_sk8I4cqgNZniYUrRoUk"
+SMTP_TIMEOUT = 200
+SMTP_HOST = "smtp.XXXX.XXX"
+SMTP_PORT = 587
+SMTP_USER = "XXXX"
+SMTP_PASSWORD = "XXXX"
+EMAIL_ADDRESS = "XXXX@XXXX.com"
+EMAIL_DISPLAY_NAME = "GeneNetwork: Local Server"
diff --git a/etc/gn-uploader-secrets.py b/etc/gn-uploader-secrets.py
new file mode 100644
index 0000000..1c5a030
--- /dev/null
+++ b/etc/gn-uploader-secrets.py
@@ -0,0 +1,3 @@
+SECRET_KEY="qQIrgiK29kXZU6v8D09y4uw_sk8I4cqgNZniYUrRoUk"
+OAUTH2_CLIENT_ID="dae2e4fc-2e32-4e87-88a0-f1e63d84470b"
+OAUTH2_CLIENT_SECRET="eNL4WZ3lpX,S>V$Bd*+p"
diff --git a/etc/gn-uploader.db b/etc/gn-uploader.db
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/etc/gn-uploader.db
diff --git a/etc/gn2-secrets.py b/etc/gn2-secrets.py
new file mode 100644
index 0000000..bc3733a
--- /dev/null
+++ b/etc/gn2-secrets.py
@@ -0,0 +1,3 @@
+SECRET_KEY="qQIrgiK29kXZU6v8D09y4uw_sk8I4cqgNZniYUrRoUk"
+OAUTH2_CLIENT_ID="0bbfca82-d73f-4bd4-a140-5ae7abb4a64d"
+OAUTH2_CLIENT_SECRET="qQIrgiK29kXZU6v8D09y4uw_sk8I4cqgNZniYUrRoUk"
diff --git a/etc/gn3-secrets.py b/etc/gn3-secrets.py
new file mode 100644
index 0000000..8f945a7
--- /dev/null
+++ b/etc/gn3-secrets.py
@@ -0,0 +1,7 @@
+SECRET_KEY="random-key"
+OAUTH2_CLIENT_ID="0bbfca82-d73f-4bd4-a140-5ae7abb4a64d"
+OAUTH2_CLIENT_SECRET="yadabadaboo"
+SPARQL_USER="dba"
+SPARQL_PASSWORD="dba"
+SPARQL_AUTH_URI="http://localhost:7082/sparql-auth/"
+FAHAMU_AUTH_TOKEN = "XXXX"
diff --git a/etc/llm.db b/etc/llm.db
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/etc/llm.db
diff --git a/genenetwork-development.scm b/genenetwork-development.scm
index 50d72a5..0b22f77 100644
--- a/genenetwork-development.scm
+++ b/genenetwork-development.scm
@@ -241,7 +241,7 @@ executed."
(setenv "GN3_LOCAL_URL" (string-append "http://localhost:" (number->string #$gn3-port)))
(setenv "GENENETWORK_FILES" #$genotype-files)
(setenv "HOME" "/tmp")
- (setenv "SQL_URI" "mysql://webqtlout:webqtlout@localhost/db_webqtl?unix_socket=/run/mysqld/mysqld.sock")
+ (setenv "SQL_URI" "mysql://webqtlout:webqtlout@localhost/db_webqtl_local?unix_socket=/run/mysqld/mysqld.sock")
(chdir "genenetwork2")
;; XXXX: FIXME: R/Qtl tests fail because files are generated in
;; the "/tmp" directory. Currently, "/tmp" is mapped by gn2/gn3
@@ -277,7 +277,7 @@ genenetwork3 source from the latest commit of @var{project}."
(setenv "PYTHONPATH" (getcwd))
(invoke "./scripts/index-genenetwork" "create-xapian-index"
xapian-build-directory
- "mysql://webqtlout:webqtlout@localhost/db_webqtl?unix_socket=/run/mysqld/mysqld.sock"
+ "mysql://webqtlout:webqtlout@localhost/db_webqtl_local?unix_socket=/run/mysqld/mysqld.sock"
"http://localhost:9082/sparql")
;; Stop genenetwork3, replace old xapian index and
;; start genenetwork3.
@@ -322,7 +322,7 @@ genenetwork3 source from the latest commit of @var{project}."
(system* (string-append gn3-dir "/scripts/index-genenetwork")
"is-data-modified"
#$%xapian-directory
- "mysql://webqtlout:webqtlout@localhost/db_webqtl?unix_socket=/run/mysqld/mysqld.sock"
+ "mysql://webqtlout:webqtlout@localhost/db_webqtl_local?unix_socket=/run/mysqld/mysqld.sock"
"http://localhost:9082/sparql"))))
(setenv "LAMINAR_REASON" "Nightly xapian index rebuild")
(invoke #$(file-append laminar "/bin/laminarc")
@@ -505,7 +505,7 @@ server described by CONFIG, a <genenetwork-configuration> object."
"\"\n"
"GN_SERVER_URL=\"https://cd.genenetwork.org/api3/\"\n"
"AUTH_SERVER_URL=\"https://auth-cd.genenetwork.org/\"\n"
- "SQL_URI=\"mysql://webqtlout:webqtlout@localhost/db_webqtl?unix_socket=/run/mysqld/mysqld.sock\"\n"
+ "SQL_URI=\"mysql://webqtlout:webqtlout@localhost/db_webqtl_local?unix_socket=/run/mysqld/mysqld.sock\"\n"
"SSL_PRIVATE_KEY=\"" gn2-secrets "/gn2-ssl-private-key.pem\"\n"
"AUTH_SERVER_SSL_PUBLIC_KEY=\"" gn2-secrets "/gn-auth-ssl-public-key.pem\"\n"))
@@ -1372,7 +1372,7 @@ gn-auth."
(number-of-buffers 4000000)
(maximum-dirty-buffers 3000000)
(server-port 9081)
- (dirs-allowed "/var/lib/data")
+ (dirs-allowed (list "/var/lib/data"))
(http-server-port %virtuoso-sparql-port)))
(service genenetwork-service-type
(genenetwork-configuration
@@ -1399,8 +1399,12 @@ gn-auth."
(hosts
(list (tissue-host
(name "issues.genenetwork.org")
- (user "laminar")
- (upstream-repository "https://github.com/genenetwork/gn-gemtext-threads"))))))
+ (projects (list (tissue-project
+ (name "issues.genenetwork.org")
+ (user "laminar")
+ (base-path "/")
+ (upstream-repository
+ "https://github.com/genenetwork/gn-gemtext-threads")))))))))
(service forge-nginx-service-type
(forge-nginx-configuration
(http-listen (forge-ip-socket
diff --git a/genenetwork-local-container.scm b/genenetwork-local-container.scm
new file mode 100644
index 0000000..d5848b7
--- /dev/null
+++ b/genenetwork-local-container.scm
@@ -0,0 +1,662 @@
+;;; genenetwork-machines --- Guix configuration for genenetwork machines
+;;; Copyright © 2025 Munyoki Kilyungi <me@bonfacemunyoki.com>
+;;;
+;;; 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/>.
+
+;;; This is the production genenetwork container currently deployed on
+;;; tux04.
+
+(use-modules (gnu)
+ (gnu build linux-container)
+ ((gn packages genenetwork) #:select (genenetwork2 genenetwork3 gn-auth gn-libs gn-uploader))
+ ((gnu packages admin) #:select (shepherd shadow))
+ ((gnu packages certs) #:select (nss-certs))
+ ((gnu packages bash) #:select (bash))
+ ((gnu packages check) #:select (python-pylint python-hypothesis))
+ ((gnu packages python-check) #:select (python-mypy python-mypy-extensions))
+ ((gnu packages python-web) #:select (gunicorn python-flask python-flask-cors))
+ ((gnu packages version-control) #:select (git-minimal))
+ ((gn packages guile) #:select (gn-guile))
+ (gn services databases)
+ (gnu services databases)
+ (guix modules)
+ (gnu services shepherd)
+ (guix search-paths)
+ (guix least-authority)
+ (guix packages)
+ (guix profiles)
+ (guix records)
+ (forge socket)
+ (forge utils)
+ (srfi srfi-1)
+ (ice-9 match))
+
+(define-record-type* <genenetwork-configuration>
+ genenetwork-configuration make-genenetwork-configuration
+ genenetwork-configuration?
+ (gn2-port genenetwork-configuration-gn2-port
+ (default 8082))
+ (gn3-port genenetwork-configuration-gn3-port
+ (default 8083))
+ (gn-auth-port genenetwork-configuration-gn-auth-port
+ (default 8084))
+ (gn2-secrets genenetwork-configuration-gn2-secrets
+ (default "/etc/genenetwork/conf/gn2/secrets.py"))
+ (gn3-secrets genenetwork-configuration-gn3-secrets
+ (default "/etc/genenetwork/conf/gn3/secrets.py"))
+ (gn-auth-secrets genenetwork-configuration-gn-auth-secrets
+ (default "/etc/genenetwork/conf/gn-auth/secrets.py"))
+ (genotype-files genenetwork-configuration-genotype-files
+ (default "/var/genenetwork/genotype-files"))
+ (sparql-endpoint genenetwork-configuration-sparql-endpoint
+ (default "http://localhost:7082/sparql"))
+ (data-directory genenetwork-data-directory
+ (default "/var/genenetwork"))
+ (xapian-db-path genenetwork-xapian-db-path
+ (default "/var/lib/xapian"))
+ (auth-db-path genenetwork-auth-db-path
+ (default "/var/genenetwork/auth.db"))
+ (llm-db-path genenetwork-llm-db-path
+ (default "/var/lib/genenetwork-sqlite/llm.db"))
+ (gn-guile-port genenetwork-configuration-gn-guile-port
+ (default 8091))
+ (gn-doc-git-checkout genenetwork-configuration-gn-doc-git-checkout
+ (default "/var/lib/gn-docs"))
+ (sql-uri genenetwork-configuration-sql-uri
+ (default
+ "mysql://webqtlout:webqtlout@localhost/db_webqtl_local?unix_socket=/run/mysqld/mysqld.sock&charset=utf8"))
+ (gn-uploader-data-directory genenetwork-configuration-gn-uploader-data-directory
+ (default "/var/lib/gn-uploader/data"))
+ (gn-uploader-port genenetwork-configuration-gn-uploader-port
+ (default 8085))
+ (gn-uploader-secrets genenetwork-configuration-gn-uploader-configuration-secrets
+ (default "/etc/genenetwork/conf/gn-uploader/secrets.py"))
+ (gn-uploader-sessions-dir genenetwork-configuration-gn-uploader-sessions-dir
+ (default "/var/lib/gn-uploader/sessions")))
+
+
+(define (genenetwork2-gexp config)
+ "Return a G-expression that runs the latest genenetwork2 development
+server described by CONFIG, a <genenetwork-configuration> object."
+ (match-record config <genenetwork-configuration>
+ (sql-uri gn2-port gn2-secrets gn3-port gn-auth-port genotype-files)
+ (with-packages
+ (list coreutils git-minimal gunicorn nss-certs)
+ (with-imported-modules '((guix build utils))
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 ftw)
+ (ice-9 match))
+
+ ;; Override the genenetwork3 used by genenetwork2.
+ (setenv "GN3_PYTHONPATH" "/genenetwork3")
+ (setenv "GN2_PROFILE" #$(profile
+ (content (package->development-manifest genenetwork2))
+ (allow-collisions? #t)))
+ (setenv "REQUESTS_CA_BUNDLE" (string-append
+ (getenv "GN2_PROFILE")
+ "/etc/ssl/certs/ca-certificates.crt"))
+ (unless (file-exists? "/etc/genenetwork/conf/gn2.conf")
+ (with-output-to-file "/etc/genenetwork/conf/gn2.conf"
+ (lambda ()
+ (display #$(string-append
+ "GN2_SECRETS=\"" gn2-secrets "\"\n"
+ "AI_SEARCH_ENABLED=True\n"
+ "TEST_FEATURE_SWITCH=True\n"
+ "GN3_LOCAL_URL=\"http://localhost:" (number->string gn3-port) "\"\n"
+ "GN_SERVER_URL=\"http://localhost:" (number->string gn3-port) "/api/\"\n"
+ "AUTH_SERVER_URL=\"http://localhost:" (number->string gn-auth-port) "\"\n"
+ "SQL_URI=\"" sql-uri "\"\n"
+ "SSL_PRIVATE_KEY=\"/etc/genenetwork/conf/gn2/private.pem\"\n"
+ "AUTH_SERVER_SSL_PUBLIC_KEY=\"/etc/genenetwork/conf/gn-auth/clients-public-keys/gn-auth.pem\"\n"))
+ ;; We actually set here twice so that we can refer to it
+ ;; when running things manually.
+ (display "GN2_PROFILE=\"")
+ (display #$(file-append (profile
+ (content (package->development-manifest genenetwork2))
+ (allow-collisions? #t))
+ "\"\n")))))
+ (setenv "GN2_SETTINGS" "/etc/genenetwork/conf/gn2.conf")
+ ;; Start genenetwork2.
+ (with-directory-excursion "/genenetwork2"
+ (invoke #$(file-append bash "/bin/sh")
+ "bin/genenetwork2" "gn2/default_settings.py" "-gunicorn-dev")))))))
+
+(define (genenetwork3-gexp config)
+ "Return a G-expression that runs the latest genenetwork3 development
+server described by CONFIG, a <genenetwork-configuration> object."
+ (match-record config <genenetwork-configuration>
+ (sql-uri gn3-port gn3-secrets gn-auth-port sparql-endpoint data-directory xapian-db-path auth-db-path llm-db-path)
+ (with-manifest (package->development-manifest genenetwork3)
+ (with-imported-modules '((guix build utils))
+ #~(begin
+ (use-modules (guix build utils))
+ ;; Configure genenetwork3.
+ (setenv "RSCRIPT" #$(file-append
+ (profile
+ (content (package->development-manifest genenetwork3))
+ (allow-collisions? #t))
+ "/bin/Rscript"))
+ (unless (file-exists? "/etc/genenetwork/conf/gn3.conf")
+ (with-output-to-file "/etc/genenetwork/conf/gn3.conf"
+ (lambda ()
+ (display #$(string-append
+ "SPARQL_ENDPOINT=\"" sparql-endpoint "\"\n"
+ "DATA_DIR=\"" data-directory "\"\n"
+ "AUTH_SERVER_URL=\"http://localhost:8084/\"\n"
+ "XAPIAN_DB_PATH=\"" xapian-db-path "\"\n"
+ "AUTH_DB=\"" auth-db-path "\"\n"
+ "LLM_DB_PATH=\"" llm-db-path "\"\n"))
+ (display "GN3_PROFILE=\"")
+ (display #$(file-append (profile
+ (content (package->development-manifest genenetwork3))
+ (allow-collisions? #t))
+ "\"\n"))
+ (display "R_SCRIPT=\"")
+ (display #$(file-append
+ (profile
+ (content (package->development-manifest genenetwork3))
+ (allow-collisions? #t))
+ "/bin/Rscript\"\n")))))
+ (setenv "GN3_PROFILE" #$(profile
+ (content (package->development-manifest genenetwork3))
+ (allow-collisions? #t)))
+ (setenv "REQUESTS_CA_BUNDLE" (string-append
+ (getenv "GN3_PROFILE")
+ "/etc/ssl/certs/ca-certificates.crt"))
+ (setenv "GN3_CONF" "/etc/genenetwork/conf/gn3.conf")
+ (setenv "GN3_SECRETS" #$gn3-secrets)
+ (setenv "HOME" "/tmp")
+ (setenv "SQL_URI" #$sql-uri)
+
+ (setenv "FLASK_ENV" "development")
+ (setenv "FLASK_DEBUG" "1")
+ ;; Run genenetwork3.
+ (with-directory-excursion "/genenetwork3"
+ (invoke #$(file-append python-flask "/bin/flask")
+ "run"
+ #$(string-append "--port=" (number->string gn3-port)))))))))
+
+(define (gn-auth-gexp config)
+ "Return a G-expression that runs the latest gn-auth development
+server described by CONFIG, a <genenetwork-configuration> object."
+ (match-record config <genenetwork-configuration>
+ (sql-uri gn-auth-port auth-db-path gn-auth-secrets)
+ (with-manifest (package->development-manifest gn-auth)
+ (with-packages (list git-minimal nss-certs)
+ (with-imported-modules '((guix build utils))
+ #~(begin
+ (use-modules (guix build utils))
+ ;; Configure gn-auth.
+ (unless (file-exists? "/etc/genenetwork/conf/gn-auth.conf")
+ (with-output-to-file "/etc/genenetwork/conf/gn-auth.conf"
+ (lambda ()
+ (display #$(string-append
+ "LOGLEVEL=\"DEBUG\"\n"
+ "SQL_URI=\"" sql-uri "\"\n"
+ "AUTH_DB=\"" auth-db-path "\"\n"
+ "GN_AUTH_SECRETS=\"" gn-auth-secrets "\"\n"))
+ (display "GN_AUTH_PROFILE=\"")
+ (display #$(file-append (profile
+ (content (package->development-manifest gn-auth))
+ (allow-collisions? #t))
+ "\"\n")))))
+ (setenv "GN_AUTH_PROFILE" #$(profile
+ (content (package->development-manifest gn-auth))
+ (allow-collisions? #t)))
+ (setenv "REQUESTS_CA_BUNDLE" (string-append
+ (getenv "GN_AUTH_PROFILE")
+ "/etc/ssl/certs/ca-certificates.crt"))
+ (setenv "GN_AUTH_CONF" "/etc/genenetwork/conf/gn-auth.conf")
+ (setenv "HOME" "/tmp")
+ (setenv "AUTHLIB_INSECURE_TRANSPORT" "true")
+ ;; Run gn-auth.
+ (with-directory-excursion "/gn-auth"
+ (invoke #$(file-append gunicorn "/bin/gunicorn")
+ "-b" #$(string-append "localhost:" (number->string gn-auth-port))
+ "--workers" "8"
+ "gn_auth.wsgi:app"))))))))
+
+(define (gn-uploader-gexp config)
+ (match-record
+ config <genenetwork-configuration>
+ (sql-uri gn-uploader-port gn-uploader-data-directory
+ gn-uploader-secrets gn-auth-port
+ gn2-port gn-uploader-sessions-dir)
+ (with-manifest
+ (package->development-manifest gn-uploader)
+ (with-imported-modules '((guix build utils))
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 ftw))
+ (unless (file-exists? "/etc/genenetwork/conf/gn-uploader.conf")
+ (with-output-to-file "/etc/genenetwork/conf/gn-uploader.conf"
+ (lambda ()
+ (display #$(string-append
+ "UPLOADER_SECRETS=\"" gn-uploader-secrets "\"\n"
+ "SQL_URI=\"" sql-uri "\"\n"
+ "ASYNCHRONOUS_JOBS_SQLITE_DB=\"/var/genenetwork/gn-uploader.db\"\n"
+ "UPLOAD_FOLDER=\"" gn-uploader-data-directory "\"\n"
+ "AUTH_SERVER_URL=\"http://localhost:" (number->string gn-auth-port) "\"\n"
+ "GN2_SERVER_URL=\"http://localhost:" (number->string gn2-port) "\"\n"
+ "SESSION_FILESYSTEM_CACHE_PATH=\"" gn-uploader-sessions-dir "\"\n"))
+ (display "GN_UPLOADER_PROFILE=\"")
+ (display #$(file-append (profile
+ (content (package->development-manifest gn-uploader))
+ (allow-collisions? #t))
+ "\"\n")))))
+ ;; Run gn-uploader
+ (with-directory-excursion "/gn-uploader"
+ ;; Configure Uploader
+ (setenv "UPLOADER_CONF" "/etc/genenetwork/conf/gn-uploader.conf")
+ (setenv "FLASK_APP" "scripts/qcapp_wsgi.py")
+ (setenv "FLASK_ENV" "development")
+ (setenv "FLASK_DEBUG" "1")
+ (setenv "ASYNCHRONOUS_JOBS_SQLITE_DB" "/var/genenetwork/gn-uploader.db")
+ (setenv "GN_UPLOADER_ENVIRONMENT"
+ #$(profile
+ (content (package->development-manifest gn-uploader))
+ (allow-collisions? #t)))
+ (invoke #$(file-append (profile
+ (content (package->development-manifest gn-uploader))
+ (allow-collisions? #t))
+ "/bin/flask")
+ "run"
+ #$(string-append "--port=" (number->string gn-uploader-port)))))))))
+
+(define (genenetwork-activation config)
+ (match-record
+ config <genenetwork-configuration>
+ (gn2-secrets
+ gn3-secrets gn3-port gn-auth-port
+ auth-db-path gn-auth-secrets gn-doc-git-checkout
+ gn-uploader-data-directory
+ gn-uploader-secrets
+ gn-uploader-sessions-dir)
+ (with-imported-modules '((guix build utils))
+ #~(begin
+ (use-modules (guix build utils)
+ (ice-9 ftw))
+ ;; Set ownership of files.
+ (for-each (lambda (file)
+ (when (eq? (stat:type (stat file)) 'directory)
+ (chmod file #o755))
+ (chown file
+ (passwd:uid (getpw "genenetwork"))
+ (passwd:gid (getpw "genenetwork"))))
+ (append
+ '("/etc/genenetwork/conf" "/genenetwork2/flask_session"
+ "/gn-uploader/flask_session")
+ (find-files #$(dirname gn-auth-secrets)
+ #:directories? #t)
+ (find-files #$(dirname gn2-secrets)
+ #:directories? #t)
+ (find-files #$(dirname gn3-secrets)
+ #:directories? #t)
+ (find-files #$(dirname gn-uploader-secrets)
+ #:directories? #t)
+ (find-files #$(dirname auth-db-path)
+ #:directories? #t)
+ (find-files #$(dirname gn-doc-git-checkout)
+ #:directories? #t)
+ (find-files #$(dirname gn-uploader-data-directory)
+ #:directories? #t)
+ (find-files #$(dirname gn-uploader-sessions-dir)
+ #:directories? #t)))
+ ;; Prevent other users from reading secret files.
+ (for-each (lambda (file)
+ (chmod file #o600))
+ (append
+ (find-files #$gn-auth-secrets
+ #:directories? #f)
+ (find-files #$gn2-secrets
+ #:directories? #f)
+ (find-files #$gn3-secrets
+ #:directories? #f)
+ (find-files #$gn-uploader-secrets
+ #:directories? #f)))))))
+
+(define (gn-guile-gexp gn-guile-port)
+ (with-imported-modules '((guix build utils))
+ #~(begin
+ (use-modules (guix build utils))
+ (let ((current-repo-path (string-append (getcwd) "/gn-docs")))
+ (when (file-exists? current-repo-path)
+ (delete-file-recursively current-repo-path))
+ (setenv "CURRENT_REPO_PATH" current-repo-path)
+ (invoke #$(file-append git-minimal "/bin/git")
+ "clone" "--depth" "1" (getenv "CGIT_REPO_PATH")))
+ (invoke #$(file-append gn-guile "/bin/gn-guile")
+ (number->string #$gn-guile-port)))))
+
+
+(define (genenetwork-shepherd-services config)
+ "Return shepherd services to run the genenetwork development server
+described by CONFIG, a <genenetwork-configuration> object."
+ (match-record config <genenetwork-configuration>
+ (gn2-port
+ gn3-port gn-auth-port genotype-files
+ data-directory xapian-db-path gn2-secrets
+ auth-db-path gn-auth-secrets
+ llm-db-path gn-doc-git-checkout
+ gn-guile-port
+ gn-uploader-port
+ gn-uploader-data-directory
+ gn-uploader-sessions-dir)
+ (list (shepherd-service
+ (documentation "Run gn-guile server.")
+ (provision '(gn-guile))
+ (requirement '(networking))
+ (modules '((ice-9 match)
+ (srfi srfi-1)))
+ (start
+ (let* ((gn-guile-settings
+ `(("CGIT_REPO_PATH" ,gn-doc-git-checkout)
+ ("LC_ALL" "en_US.UTF-8")
+ ("GIT_COMMITTER_NAME" "genenetwork")
+ ("GIT_COMMITTER_EMAIL" "no-reply@git.genenetwork.org"))))
+ #~(make-forkexec-constructor
+ (list #$(least-authority-wrapper
+ (program-file "gn-guile"
+ (gn-guile-gexp gn-guile-port))
+ #:name "gn-guile-pola-wrapper"
+ #:preserved-environment-variables
+ (map first gn-guile-settings)
+ #:mappings (list (file-system-mapping
+ (source gn-doc-git-checkout)
+ (target source)
+ (writable? #t)))
+ #:namespaces (delq 'net %namespaces))
+ "127.0.0.1" #$(number->string gn-guile-port))
+ #:user "genenetwork"
+ #:group "genenetwork"
+ #:environment-variables
+ (map (match-lambda
+ ((spec value)
+ (string-append spec "=" value)))
+ '#$gn-guile-settings)
+ #:log-file "/var/log/gn-guile.log")))
+ (stop #~(make-kill-destructor)))
+ (shepherd-service
+ (documentation "Run GeneNetwork 2 development server.")
+ (provision '(genenetwork2))
+ ;; FIXME: The genenetwork2 service should depend on redis.
+ (requirement '(networking genenetwork3))
+ (modules '((guix search-paths)
+ (ice-9 match)
+ (srfi srfi-1)))
+ (start
+ (let* ((gn2-manifest (packages->manifest (list genenetwork2)))
+ (gn2-profile (profile
+ (content gn2-manifest)
+ (allow-collisions? #t)))
+ (gn2-settings
+ `(("SERVER_PORT" ,(number->string gn2-port))
+ ("GENENETWORK_FILES" ,genotype-files)
+ ("HOME" "/tmp")
+ ("LC_ALL" "en_US.UTF-8")
+ ("NO_REDIS" "no-redis")
+ ("RUST_BACKTRACE" "1"))))
+ (with-imported-modules (source-module-closure '((guix search-paths)))
+ #~(make-forkexec-constructor
+ (list #$(least-authority-wrapper
+ (program-file "genenetwork2"
+ (genenetwork2-gexp config))
+ #:name "genenetwork2-pola-wrapper"
+ #:preserved-environment-variables
+ (append '("REQUESTS_CA_BUNDLE")
+ (map first gn2-settings)
+ (map search-path-specification-variable
+ (manifest-search-paths gn2-manifest)))
+ ;; If we mapped only the mysqld.sock
+ ;; socket file, it would break when the
+ ;; external mysqld server is restarted.
+ #:mappings (list (file-system-mapping
+ (source genotype-files)
+ (target source))
+ (file-system-mapping
+ (source "/genenetwork2")
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source "/run/mysqld")
+ (target source)
+ (writable? #t))
+ ;; XXXX: FIXME: R/Qtl generates
+ ;; files in "/tmp" and
+ ;; "/tmp/gn2". These files are
+ ;; accessed by gn3 for R/Qtl
+ ;; mapping
+ (file-system-mapping
+ (source "/tmp")
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source "/etc/genenetwork/conf")
+ (target source)
+ (writable? #t)))
+ #:namespaces (delq 'net %namespaces))
+ "127.0.0.1" #$(number->string gn2-port))
+ #:user "genenetwork"
+ #:group "genenetwork"
+ #:environment-variables
+ (append
+ '("REQUESTS_CA_BUNDLE="
+ #$(file-append gn2-profile "/etc/ssl/certs/ca-certificates.crt"))
+ (map (match-lambda
+ ((spec . value)
+ (string-append (search-path-specification-variable spec)
+ "="
+ value)))
+ (evaluate-search-paths
+ (map sexp->search-path-specification
+ '#$(map search-path-specification->sexp
+ (manifest-search-paths gn2-manifest)))
+ (list #$gn2-profile)))
+ (map (match-lambda
+ ((spec value)
+ (string-append spec "=" value)))
+ '#$gn2-settings))
+ #:log-file "/var/log/genenetwork2.log"))))
+ (stop #~(make-kill-destructor)))
+ (shepherd-service
+ (documentation "Run GeneNetwork 3 development server.")
+ (provision '(genenetwork3))
+ (requirement '(networking))
+ (start #~(make-forkexec-constructor
+ (list #$(least-authority-wrapper
+ (program-file "genenetwork3"
+ (genenetwork3-gexp config))
+ #:name "genenetwork3-pola-wrapper"
+ #:mappings (list (file-system-mapping
+ (source "/genenetwork3")
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source "/run/mysqld")
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source "/tmp")
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source data-directory)
+ (target source))
+ (file-system-mapping
+ (source xapian-db-path)
+ (target source))
+ (file-system-mapping
+ (source "/etc/genenetwork/conf")
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source auth-db-path)
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source llm-db-path)
+ (target source)
+ (writable? #t)))
+ #:namespaces (delq 'net %namespaces))
+ "127.0.0.1" #$(number->string gn3-port))
+ #:user "genenetwork"
+ #:group "genenetwork"
+ #:log-file "/var/log/genenetwork3.log"))
+ (stop #~(make-kill-destructor)))
+ (shepherd-service
+ (documentation "Run gn-uploader")
+ (provision '(gn-uploader))
+ (requirement '(networking))
+ (start #~(make-forkexec-constructor
+ (list #$(least-authority-wrapper
+ (program-file "gn-uploader"
+ (gn-uploader-gexp config))
+ #:name "gn-uploader-pola-wrapper"
+ #:mappings (list (file-system-mapping
+ (source "/gn-uploader")
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source "/run/mysqld")
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source "/tmp")
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source "/var/genenetwork/gn-uploader.db")
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source "/etc/genenetwork/conf")
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source gn-uploader-data-directory)
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source gn-uploader-sessions-dir)
+ (target source)
+ (writable? #t)))
+ #:namespaces (delq 'net %namespaces))
+ "127.0.0.1" #$(number->string gn-uploader-port))
+ #:user "genenetwork"
+ #:group "genenetwork"
+ #:log-file "/var/log/gn-uploader.log"))
+ (stop #~(make-kill-destructor)))
+ (shepherd-service
+ (documentation "Run gn-auth development server.")
+ (provision '(gn-auth))
+ (requirement '(networking))
+ (start #~(make-forkexec-constructor
+ (list #$(least-authority-wrapper
+ (program-file "gn-auth"
+ (gn-auth-gexp config))
+ #:name "gn-auth-pola-wrapper"
+ ;; If we mapped only the mysqld.sock
+ ;; socket file, it would break when the
+ ;; external mysqld server is restarted.
+ #:mappings (list (file-system-mapping
+ (source "/run/mysqld")
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source "/gn-auth")
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source "/run/mysqld")
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source data-directory)
+ (target source))
+ (file-system-mapping
+ (source "/var/genenetwork")
+ (target source)
+ (writable? #t))
+ (file-system-mapping
+ (source "/etc/genenetwork/conf")
+ (target source)
+ (writable? #t)))
+ #:namespaces (delq 'net %namespaces))
+ "127.0.0.1" #$(number->string gn-auth-port))
+ #:user "genenetwork"
+ #:group "genenetwork"
+ #:log-file "/var/log/gn-auth.log"))
+ (stop #~(make-kill-destructor))))))
+
+(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-service-type
+ (service-type
+ (name 'genenetwork)
+ (description "Run GeneNetwork development servers and CI.")
+ (extensions
+ (list (service-extension account-service-type
+ (const %genenetwork-accounts))
+ (service-extension activation-service-type
+ genenetwork-activation)
+ (service-extension shepherd-root-service-type
+ genenetwork-shepherd-services)))
+ (default-value (genenetwork-configuration))))
+
+
+(operating-system
+ (host-name "genenetwork-work-container")
+ (timezone "UTC")
+ (locale "en_US.utf8")
+ (bootloader (bootloader-configuration
+ (bootloader grub-bootloader)
+ (targets (list "/dev/sdX"))))
+ (file-systems %base-file-systems)
+ (users %base-user-accounts)
+ (sudoers-file
+ (mixed-text-file "sudoers"
+ "@include " %sudoers-specification
+ "\nacme ALL = NOPASSWD: " (file-append shepherd "/bin/herd") " restart nginx\n"))
+ (packages (cons* genenetwork2 python-hypothesis gn-auth
+ python-mypy python-mypy-extensions python-pylint
+ %base-packages))
+ (services (cons* (service virtuoso-service-type
+ (virtuoso-configuration
+ (server-port 7081)
+ (http-server-port 7082)
+ (dirs-allowed (list "/var/lib/data"))
+ (database-file "/var/lib/virtuoso/genenetwork-virtuoso.db")
+ (transaction-file "/var/lib/virtuoso/genenetwork-virtuoso.trx")))
+ (service redis-service-type
+ (redis-configuration
+ (bind "127.0.0.1")
+ (port 6379)
+ (working-directory "/var/lib/redis")))
+ (service genenetwork-service-type)
+ %base-services)))
diff --git a/genenetwork-local-container.sh b/genenetwork-local-container.sh
new file mode 100755
index 0000000..7632d8a
--- /dev/null
+++ b/genenetwork-local-container.sh
@@ -0,0 +1,650 @@
+#! /bin/sh -e
+
+# genenetwork-machines --- Guix configuration for genenetwork machines
+# Copyright © 2025 Munyoki Kilyungi <me@bonfacemunyoki.com>
+#
+# 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/>.
+
+# Build and install genenetwork container on your local machine.
+
+set -euo pipefail
+
+
+BASE_DIR="${HOME:-/home/$USER}/genenetwork"
+SYSTEM_DIRECTORIES=(
+ "$BASE_DIR"
+ "$BASE_DIR/var/log"
+ "$BASE_DIR/var/genenetwork"
+ "$BASE_DIR/etc/genenetwork/conf"
+ "$BASE_DIR/etc/genenetwork"
+ "$BASE_DIR/var/lib/redis"
+ "$BASE_DIR/var/lib/virtuoso"
+ "$BASE_DIR/var/lib/data"
+ "$BASE_DIR/var/lib/gn-uploader/data"
+ "$BASE_DIR/var/lib/gn-uploader/sessions"
+ "$BASE_DIR/var/lib/xapian"
+ "$BASE_DIR/var/genenetwork/genotype-files/genotype/json"
+ "$BASE_DIR/var/lib/genenetwork-sqlite"
+ "$BASE_DIR/var/lib/genenetwork-gnqa"
+ "/tmp/local-container"
+)
+
+GN_PROJECTS=(
+ "genenetwork2:https://github.com/genenetwork/genenetwork2.git"
+ "genenetwork3:https://github.com/genenetwork/genenetwork3.git"
+ "gn-transform-databases:https://git.genenetwork.org/gn-transform-databases/"
+ "gn-auth:https://git.genenetwork.org/gn-auth"
+ "gn-docs:https://git.genenetwork.org/gn-docs"
+ "gn-uploader:https://git.genenetwork.org/gn-uploader"
+)
+
+# File mappings: source -> destination
+declare -A FILE_MAPPINGS=(
+ ["etc/auth.db"]="$BASE_DIR/var/genenetwork/auth.db"
+ ["etc/gn-uploader.db"]="$BASE_DIR/var/genenetwork/gn-uploader.db"
+ ["etc/llm.db"]="$BASE_DIR/var/lib/genenetwork-sqlite/llm.db"
+ ["etc/gn2-secrets.py"]="$BASE_DIR/etc/genenetwork/conf/gn2/secrets.py"
+ ["etc/gn3-secrets.py"]="$BASE_DIR/etc/genenetwork/conf/gn3/secrets.py"
+ ["etc/gn-auth-secrets.py"]="$BASE_DIR/etc/genenetwork/conf/gn-auth/secrets.py"
+ ["etc/gn-uploader-secrets.py"]="$BASE_DIR/etc/genenetwork/conf/gn-uploader/secrets.py"
+)
+
+CONTAINER_SCM="genenetwork-local-container.scm"
+CONTAINER_BIN="/usr/local/bin/genenetwork-local-container"
+GC_ROOT="/var/guix/gcroots/genenetwork-local-container"
+
+log() {
+ local level="$1"
+ shift
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] $level: $*" >&2
+}
+
+# Check dependencies
+for cmd in git sudo diff cp grep; do
+ if ! command -v "$cmd" &>/dev/null; then
+ log "ERROR" "Required command '$cmd' not found"
+ exit 1
+ fi
+done
+
+if ! command -v guix &>/dev/null; then
+ echo "Please install Guix to proceed. Follow the instructions at:"
+ echo "https://issues.genenetwork.org/topics/octopus/set-up-guix-for-new-users"
+ echo "After installing Guix, try running this script again."
+ exit 1
+fi
+
+# Check for gn-bioinformatics channel
+guix describe | grep gn-bioinformatics\
+ &> /dev/null && log "INFO" "guix guix-informatics $(guix describe | grep gn-bioinformatics | cut -d ' ' -f 4)" || \
+ (clear && guix describe &&
+ echo ""
+ log "ERROR" "Please make sure your current profile has gn-bioinformatics" &&
+ echo "" &&
+ echo "Refer to:" &&
+ echo " https://issues.genenetwork.org/topics/guix/guix-profiles" &&
+ echo "to help you set your profile" && echo "" &&
+ exit 1)
+
+# Validate HOME is set
+if [ -z "${HOME:-}" ]; then
+ log "ERROR" "HOME environment variable is not set"
+ exit 1
+fi
+
+init_container() {
+ log "INFO" "Creating system directories..."
+ for dir in "${SYSTEM_DIRECTORIES[@]}"; do
+ # Check if directory exists and is accessible
+ if [ -d "$dir" ]; then
+ if [ -w "$dir" ]; then
+ log "DEBUG" "Directory exists and is writable: $dir"
+ else
+ log "WARNING" "Directory exists but is not writable: $dir. Making this writable"
+ sudo chown -R "$USER" "$dir"
+ fi
+ continue
+ fi
+
+ # Attempt to create directory
+ log "INFO" "Creating directory: $dir"
+ if [ -w "$(dirname "$dir")" ]; then
+ # Parent directory is writable, try without sudo
+ if ! mkdir -p "$dir"; then
+ log "ERROR" "Failed to create directory without sudo: $dir"
+ exit 1
+ fi
+ else
+ # Parent directory requires root, use sudo
+ if ! sudo mkdir -p "$dir"; then
+ log "ERROR" "Failed to create directory with sudo: $dir"
+ exit 1
+ fi
+ # Set ownership to current user if created with sudo
+ if ! sudo chown -R "$USER" "$dir"; then
+ log "WARNING" "Failed to set ownership for: $dir"
+ fi
+ fi
+ done
+
+ # Check and copy configuration files
+ log "INFO" "Checking and copying configuration files..."
+ for src in "${!FILE_MAPPINGS[@]}"; do
+ dest="${FILE_MAPPINGS[$src]}"
+ log "INFO" "Processing $src -> $dest"
+
+ # Check if source file exists
+ if [ ! -f "$src" ]; then
+ log "ERROR" "Source file does not exist: $src"
+ exit 1
+ fi
+
+ # Check if destination file exists
+ if [ ! -f "$dest" ]; then
+ log "INFO" "Destination file does not exist, copying $src to $dest"
+ mkdir -p "$(dirname "$dest")"
+ if ! cp "$src" "$dest"; then
+ log "ERROR" "Failed to copy $src to $dest"
+ exit 1
+ fi
+ continue
+ fi
+
+ # Compare files using diff
+ log "INFO" "Comparing $src with $dest"
+ if diff_output=$(diff -u "$dest" "$src" 2>&1); then
+ log "INFO" "Files $src and $dest are identical"
+ else
+ log "INFO" "Differences found between $src and $dest:"
+ echo "$diff_output" >&2
+ log "INFO" "Copying $src to $dest"
+ if ! cp "$src" "$dest"; then
+ log "ERROR" "Failed to copy $src to $dest"
+ exit 1
+ fi
+ fi
+ done
+
+ is_git_repository() {
+ local dir="$1"
+ # Check for standard repository
+ if [ -d "$dir/.git" ]; then
+ if [ -f "$dir/.git/HEAD" ] && [ -d "$dir/.git/refs" ]; then
+ log "DEBUG" "Detected standard Git repository: $dir"
+ return 0
+ else
+ log "ERROR" "Directory $dir/.git exists but is not a valid Git repository"
+ return 1
+ fi
+ # Check for bare repository
+ elif [ -f "$dir/HEAD" ] && [ -d "$dir/refs" ] && [ -d "$dir/objects" ]; then
+ log "DEBUG" "Detected bare Git repository: $dir"
+ return 0
+ else
+ log "ERROR" "$dir exists but is not a Git repository (neither standard nor bare)"
+ return 1
+ fi
+ }
+ # Clone GeneNetwork projects
+ log "INFO" "Cloning GeneNetwork projects..."
+ for project_entry in "${GN_PROJECTS[@]}"; do
+ IFS=':' read -r project repo_url <<< "$project_entry"
+ dir="$BASE_DIR/$project"
+ if [ ! -d "$dir" ]; then
+ log "INFO" "Cloning $project from $repo_url to $dir"
+ if ! git clone "$repo_url" "$dir"; then
+ log "ERROR" "Failed to clone $project"
+ exit 1
+ fi
+ else
+ log "DEBUG" "Directory exists, skipping clone: $dir"
+ if ! is_git_repository "$dir"; then
+ log "ERROR" "$dir exists but is not a Git repository"
+ exit 1
+ fi
+ fi
+ done
+
+ FLASK_SESSIONS=(
+ "gn-uploader:$BASE_DIR/gn-uploader/flask_session"
+ "genenetwork2:$BASE_DIR/genenetwork2/flask_session"
+ )
+ for flask_session in "${FLASK_SESSIONS[@]}"; do
+ IFS=':' read -r project session_dir <<< "$flask_session"
+ if [ ! -d "$session_dir" ]; then
+ log "INFO" "Creating FLASK_SESSION directory: $session_dir"
+ if ! mkdir -p "$session_dir"; then
+ log "ERROR" "Failed to create FLASK_SESSION directory: $session_dir"
+ exit 1
+ fi
+ else
+ log "DEBUG" "FLASK_SESSION directory already exists: $session_dir"
+ fi
+ done
+
+ # Verify container SCM file exists
+ if [ ! -f "$CONTAINER_SCM" ]; then
+ log "ERROR" "Container SCM file not found: $CONTAINER_SCM"
+ exit 1
+ fi
+
+ # Create Guix system container
+ log "INFO" "Creating Guix system container..."
+ SHARE_OPTS=(
+ "--share=$BASE_DIR/var/log=/var/log"
+ "--share=$BASE_DIR/var/genenetwork=/var/genenetwork"
+ "--share=$BASE_DIR/etc/genenetwork/conf=/etc/genenetwork/conf"
+ "--share=$BASE_DIR/etc/genenetwork=/etc/genenetwork"
+ "--share=$BASE_DIR/var/lib/redis=/var/lib/redis"
+ "--share=$BASE_DIR/var/lib/virtuoso=/var/lib/virtuoso"
+ "--share=$BASE_DIR/var/lib/data=/var/lib/data"
+ "--share=$BASE_DIR/var/lib/gn-uploader/data=/var/lib/gn-uploader/data"
+ "--share=$BASE_DIR/var/lib/gn-uploader/sessions=/var/lib/gn-uploader/sessions"
+ "--share=$BASE_DIR/genenetwork2=/genenetwork2"
+ "--share=$BASE_DIR/genenetwork3=/genenetwork3"
+ "--share=$BASE_DIR/gn-uploader=/gn-uploader"
+ "--share=$BASE_DIR/gn-auth=/gn-auth"
+ "--share=$BASE_DIR/var/lib/xapian=/var/lib/xapian"
+ "--share=$BASE_DIR/var/lib/genenetwork-sqlite=/var/lib/genenetwork-sqlite"
+ "--share=$BASE_DIR/var/lib/genenetwork-gnqa=/var/lib/genenetwork-gnqa"
+ "--share=/tmp/local-container=/tmp"
+ "--share=$BASE_DIR/gn-docs=/var/lib/gn-docs"
+ "--share=/run/mysqld=/run/mysqld"
+ )
+
+ container_script=$(guix system container \
+ --network --fallback \
+ --load-path=. \
+ --verbosity=3 \
+ "${SHARE_OPTS[@]}" \
+ "$CONTAINER_SCM")
+
+ log $container_script
+
+ # Create symbolic links
+ log "INFO" "Creating symbolic links..."
+ if ! sudo ln -sf "$container_script" "$CONTAINER_BIN"; then
+ log "ERROR" "Failed to create symbolic link: $CONTAINER_BIN"
+ exit 1
+ fi
+
+ if ! sudo ln -sf "$container_script" "$GC_ROOT"; then
+ log "ERROR" "Failed to create GC root link: $GC_ROOT"
+ exit 1
+ fi
+
+ log "INFO" "Setup completed successfully!"
+ log "INFO" "Container script: $container_script"
+ log "INFO" "Run with: $CONTAINER_BIN"
+ log "INFO" "Email: test@development.user"
+ log "INFO" "Password: testpasswd"
+}
+
+if [ "$1" = "--init-container" ]; then
+ init_container
+ exit 0
+fi
+
+init_sql() {
+ # Configuration
+ URL="https://files.genenetwork.org/database/db_webqtl_s-2025-02-18.sql.xz"
+ DOWNLOAD_DIR="/tmp"
+ FILE_NAME=$(basename "$URL")
+ EXTRACTED_FILE="${FILE_NAME%.xz}"
+ DB_USER="webqtlout"
+ DB_PASSWORD="webqtlout"
+ DB_HOST="localhost"
+ DB_NAME="db_webqtl_local"
+ MYSQL_ROOT_USER="" # Change to your MySQL admin user if different
+ MYSQL_ROOT_PASSWORD="" # Set this or leave empty to prompt
+
+ # Check for required tools
+ for cmd in wget xz mysql; do
+ if ! command -v "$cmd" &> /dev/null; then
+ log ERROR "Required command '$cmd' not found"
+ exit 1
+ fi
+ done
+
+ # Extract the .xz file
+ if [ ! -f "/tmp/db_webqtl_s-2025-02-18.sql" ]; then
+ # Download the file
+ log INFO "Downloading $URL to $DOWNLOAD_DIR/$FILE_NAME"
+ if ! wget -O "$DOWNLOAD_DIR/$FILE_NAME" "$URL"; then
+ log ERROR "Failed to download $URL"
+ exit 1
+ fi
+
+ log INFO "Extracting $DOWNLOAD_DIR/$FILE_NAME"
+ if ! xz -d "$DOWNLOAD_DIR/$FILE_NAME"; then
+ log ERROR "Failed to extract $DOWNLOAD_DIR/$FILE_NAME"
+ rm -f "$DOWNLOAD_DIR/$FILE_NAME"
+ exit 1
+ fi
+ fi
+
+ # Prepare MySQL user and password credentials
+ if [ -z "$MYSQL_ROOT_USER" ]; then
+ log INFO "MySQL root user not set, prompting for input"
+ read -s -p "Enter MySQL user: " MYSQL_ROOT_USER
+ fi
+ if [ -z "$MYSQL_ROOT_PASSWORD" ]; then
+ log INFO "MySQL root password not set, prompting for input"
+ read -s -p "Enter MySQL root password: " MYSQL_ROOT_PASSWORD
+ fi
+
+ # Check if DB user exists, create if not
+ log INFO "Checking if MySQL user $DB_USER exists"
+ USER_EXISTS=$(mysql -h "$DB_HOST" -u "$MYSQL_ROOT_USER" -p"$MYSQL_ROOT_PASSWORD" -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$DB_USER' AND host = 'localhost') AS user_exists;" 2>/dev/null | grep -o '[0-1]$')
+ if [ "$USER_EXISTS" = "0" ]; then
+ log INFO "Creating MySQL user $DB_USER"
+ if ! mysql -h "$DB_HOST" -u "$MYSQL_ROOT_USER" -p"$MYSQL_ROOT_PASSWORD" -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASSWORD'; GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" 2>/dev/null; then
+ log ERROR "Failed to create MySQL user $DB_USER"
+ rm -f "$DOWNLOAD_DIR/$EXTRACTED_FILE"
+ exit 1
+ fi
+ else
+ log INFO "User $DB_USER already exists, ensuring privileges"
+ if ! mysql -h "$DB_HOST" -u "$MYSQL_ROOT_USER" -p"$MYSQL_ROOT_PASSWORD" -e "GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" 2>/dev/null; then
+ log ERROR "Failed to update privileges for $DB_USER"
+ rm -f "$DOWNLOAD_DIR/$EXTRACTED_FILE"
+ exit 1
+ fi
+ fi
+
+ # Create database if it doesn't exist
+ log INFO "Ensuring database $DB_NAME exists"
+ if ! mysql -h "$DB_HOST" -u "$MYSQL_ROOT_USER" -p"$MYSQL_ROOT_PASSWORD" -e "CREATE DATABASE IF NOT EXISTS $DB_NAME;" 2>/dev/null; then
+ log ERROR "Failed to create or verify database $DB_NAME"
+ rm -f "$DOWNLOAD_DIR/$EXTRACTED_FILE"
+ exit 1
+ fi
+
+ # Install the SQL dump into the database
+ log INFO "Importing $DOWNLOAD_DIR/$EXTRACTED_FILE into $DB_NAME"
+ if ! mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASSWORD" "$DB_NAME" < "$DOWNLOAD_DIR/$EXTRACTED_FILE"; then
+ log ERROR "Failed to import $DOWNLOAD_DIR/$EXTRACTED_FILE into $DB_NAME"
+ rm -f "$DOWNLOAD_DIR/$EXTRACTED_FILE"
+ exit 1
+ fi
+
+ # Clean up
+ log INFO "Removing $DOWNLOAD_DIR/$EXTRACTED_FILE"
+ rm -f "$DOWNLOAD_DIR/$EXTRACTED_FILE"
+
+ log INFO "Database import completed successfully"
+}
+
+if [ "$1" = "--init-sql" ]; then
+ init_sql
+ exit 0
+fi
+
+init_rdf() {
+ # Check for required tools
+ for cmd in guix; do
+ if ! command -v "$cmd" &> /dev/null; then
+ log ERROR "Required command '$cmd' not found"
+ exit 1
+ fi
+ done
+
+ log INFO "Please make sure your container is running for this to work"
+ log INFO "This takes some time to run"
+ # Check if directory exists and is accessible
+ if [ -d "$BASE_DIR/var/lib/data" ]; then
+ if [ -w "$BASE_DIR/var/lib/data" ]; then
+ log "DEBUG" "Directory exists and is writable: $BASE_DIR/var/lib/data"
+ else
+ log "WARNING" "Directory exists but is not writable: $BASE_DIR/var/lib/data. Making this writable"
+ sudo chown -R "$USER" "$BASE_DIR/var/lib/data"
+ fi
+ continue
+ fi
+ curr_dir="$PWD"
+ cd "$BASE_DIR/gn-transform-databases"
+ if ls $BASE_DIR/var/lib/data/*ttl >/dev/null 2>&1; then
+ log INFO "Removing all the ttl files and generating them again"
+ rm $BASE_DIR/var/lib/data/*ttl
+ fi
+
+ log INFO "Generating the ttl files"
+ guix shell -m "manifest.scm" -- guile "generate-ttl-files.scm" \
+ --settings "$curr_dir/etc/conn.scm" --output "$BASE_DIR/var/lib/data"
+ log INFO "Loading ttl files"
+ guix shell guile-dbi -m "manifest.scm" -- guile load-rdf.scm \
+ "$curr_dir/etc/conn.scm"
+ cd $curr_dir
+}
+
+if [ "$1" = "--init-rdf" ]; then
+ init_rdf
+ exit 0
+fi
+
+init_xapian() {
+ # Check for required tools
+ log INFO "Please make sure your container is running for this to work"
+ log INFO "This takes some time to run"
+ # Check if directory exists and is accessible
+ if [ -d "$BASE_DIR/var/lib/xapian" ]; then
+ if [ -w "$BASE_DIR/var/lib/xapian" ]; then
+ log "DEBUG" "Directory exists and is writable: $BASE_DIR/var/lib/xapian"
+ else
+ log "WARNING" "Directory exists but is not writable: $BASE_DIR/var/lib/xapian. Making this writable"
+ sudo chown -R "$USER" "$BASE_DIR/var/lib/xapian"
+ fi
+ fi
+ # Check if build directory exists
+ if [ ! -f "$BASE_DIR/var/lib/xapian/build" ]; then
+ rm -rf "$BASE_DIR/var/lib/xapian/build"
+ fi
+ guix shell python-wrapper genenetwork3 \
+ --share="$BASE_DIR/var/lib/xapian" -- \
+ "$BASE_DIR/genenetwork3/scripts/index-genenetwork" create-xapian-index \
+ "$BASE_DIR/var/lib/xapian/build" \
+ "mysql://webqtlout:webqtlout@localhost/db_webqtl_local?unix_socket=/run/mysqld/mysqld.sock&charset=utf8" \
+ "http://localhost:7082/sparql"
+ mv $BASE_DIR/var/lib/xapian/build/* $BASE_DIR/var/lib/xapian/ \
+ && rmdir $BASE_DIR/var/lib/xapian/build/
+ log INFO "Please restart the container to set the correct permissions"
+}
+
+if [ "$1" = "--init-xapian" ]; then
+ init_xapian
+ exit 0
+fi
+
+
+# Function to handle yes/no prompts
+prompt_yes_no() {
+ local prompt="$1"
+ while true; do
+ read -p "$prompt (yes/no): " response
+ case "$response" in
+ [Yy][Ee][Ss]|[Yy] ) return 0 ;;
+ [Nn][Oo]|[Nn] ) return 1 ;;
+ * ) echo "Please answer 'yes' or 'no'." ;;
+ esac
+ done
+}
+
+# Main tutorial function
+run_beginner_setup_tutorial() {
+ clear
+ # Step 1: Initialize Container
+ echo "Step 1: Setting Up the Container"
+ echo "--------------------------------"
+ echo "Before we begin, make sure you are using the correct guix-profile that has:"
+ echo ""
+ echo " guix-bioinformatics"
+ echo ""
+ echo "Otherwise, I will terminate early. If you don't know how to do that, read:"
+ echo ""
+ echo " https://issues.genenetwork.org/topics/guix/guix-profiles"
+ echo ""
+ echo "This step prepares the container environment where GeneNetwork runs."
+ echo "It ensures all necessary files, folders, and configurations are in place."
+ echo ""
+ echo "This step is similar to running:"
+ echo ""
+ echo "./genenetwork-local-container.sh --init-container"
+ echo ""
+ echo "NOTE: If you have any of the following projects:"
+ echo " genenetwork2"
+ echo " genenetwork3"
+ echo " gn-uploader"
+ echo ""
+ echo "Move/copy them to $BASE_DIR. This is where'll you'll be hacking"
+ echo "on them from. Because of how we mount things inside the container,"
+ echo "make sure the names appear as above."
+ echo ""
+ echo "Now, some things to note. AI search won't work. To get this working, if you want so,"
+ echo "Get into the container, and modify FAHAMU_AUTH_TOKEN in:"
+ echo " /etc/genenetwork/conf/gn3/secrets.py"
+ echo ""
+ echo "Similarly, e-mail functionality ala resetting passwords or confirming a new user"
+ echo "won't work unless you modify:"
+ echo " SMTP_HOST = 'XXXX'"
+ echo " SMTP_USER = 'XXXX'"
+ echo " SMTP_PASSWORD = 'XXXX'"
+ echo " SMTP_PORT = 'XXXX'"
+ echo " EMAIL_ADDRESS = 'XXXX'"
+ echo " EMAIL_DISPLAY_NAME = 'XXXX'"
+ echo "inside:"
+ echo " /etc/genenetwork/conf/gn-auth/secrets.py"
+ echo ""
+ if prompt_yes_no "Would you like to set up the container now?"; then
+ init_container
+ clear
+ echo "Container setup complete!"
+ echo ""
+ echo "Right now you have a working container. You need to set-up sql, upload rdf data"
+ echo "and build a xapian index to have a fully functioning system akin to production and"
+ echo "our test environment."
+ echo ""
+ else
+ clear
+ echo "Skipping container setup. Moving to the next step."
+ fi
+ echo ""
+
+ # Step 2: Initialize SQL Database
+ echo "Step 2: Setting Up the SQL Database"
+ echo "-----------------------------------"
+ echo "Please make sure you have a working MariaDB installation on your Linux system."
+ echo ""
+ echo "This step creates and configures the database for storing GeneNetwork data."
+ echo ""
+ echo "It sets up tables and initial data needed for Genenetwork to run"
+ echo ""
+ echo "This step is similar to running:"
+ echo "./genenetwork-local-container.sh --init-sql"
+ echo ""
+ echo "Make sure you have a running mysql instance for this to work"
+ echo ""
+ if prompt_yes_no "Would you like to set up the database now?"; then
+ init_sql
+ clear
+ echo "Database setup complete!"
+ else
+ clear
+ echo "Skipping database setup. Moving to the next step: Importing RDF"
+ fi
+ echo ""
+
+ # Step 3: Initialize RDF
+ echo ""
+ echo "Step 3: Importing RDF into your local set-up"
+ echo "---------------------------------------------"
+ echo "This step adds RDF (Resource Description Framework) data."
+ echo ""
+ echo "RDF helps organize and link data for GeneNetwork."
+ echo ""
+ echo "IMPORTANT. This step requires you to have a running container instance."
+ echo " Otherwise, it will fail."
+ echo "To run your container, in a different shell, run:"
+ echo "sudo /usr/local/bin/genenetwork-local-container"
+ echo ""
+ echo "This step is similar to running:"
+ echo "./genenetwork-local-container.sh --init-rdf"
+ echo ""
+ if prompt_yes_no "Would you like to set up RDF now?"; then
+ init_rdf
+ clear
+ echo "RDF setup complete!"
+ else
+ clear
+ echo "Skipping RDF setup."
+ fi
+ echo ""
+
+ # Step 4: Initialize XAPIAN
+ clear
+ echo "Step 3: Setting up XAPIAN search locally"
+ echo "---------------------------------------------"
+ echo "This indexes all your data from GeneNetwork and indexes it using XAPIAN"
+ echo ""
+ echo "Read more here:"
+ echo "https://issues.genenetwork.org/topics/xapian/xapian-search"
+ echo ""
+ echo "IMPORTANT. This step requires you to have a running container instance."
+ echo " Otherwise, it will fail."
+ echo " To run your container, in a different shell, run:"
+ echo " sudo /usr/local/bin/genenetwork-local-container"
+ echo ""
+ echo "This step is similar to running:"
+ echo "./genenetwork-local-container.sh --init-xapian"
+ echo ""
+ if prompt_yes_no "Would you like to build the xapian index now?"; then
+ init_xapian
+ clear
+ echo "XAPIAN setup complete!"
+ else
+ clear
+ echo "Skipping RDF setup."
+ fi
+ echo ""
+
+ # Completion Message
+ echo "============================================================="
+ echo "Congratulations! You've completed the GeneNetwork Beginner Setup Tutorial!"
+ echo "============================================================="
+ echo "You may have skipped some steps. To complete them, run the tutorial again."
+ echo "Your environment is ready to the extent you chose to set it up."
+ echo "If you run your container, you can access the different web-services at:"
+ echo "gn-guile : localhost:8091"
+ echo "genenetwork2 : localhost:8082"
+ echo "genenetwork3 : localhost:8083"
+ echo "gn-uploader : localhost:8085"
+ echo "sparql web ui: localhost:7082/sparql"
+ echo ""
+ echo "To access virtuoso back-end service, run:"
+ echo "guix shell virtuoso-ose -- isql 7081"
+ echo ""
+ echo "To login in gn-uploader/genenetwork2, use the following credentials:"
+ echo " Email: test@development.user"
+ echo " Password: testpasswd"
+ exit 0
+}
+
+# Check for --beginner-set-up flag or run tutorial interactively
+if [ "$1" = "--beginner-set-up" ]; then
+ # Interactive mode: run the tutorial
+ run_beginner_setup_tutorial
+ exit 0
+fi
diff --git a/genenetwork/services/genenetwork.scm b/genenetwork/services/genenetwork.scm
index b863bcf..a403f21 100644
--- a/genenetwork/services/genenetwork.scm
+++ b/genenetwork/services/genenetwork.scm
@@ -109,6 +109,8 @@
(default "/var/empty"))
(gn3-data-directory genenetwork-configuration-gn3-data-directory
(default "/var/genenetwork"))
+ (gn2-sessions-dir genenetwork-configuration-gn2-sessions-dir
+ (default "/var/genenetwork/sessions/genenetwork2"))
(gn2-secrets genenetwork-configuration-gn2-secrets
(default "/etc/genenetwork"))
(gn3-secrets genenetwork-configuration-gn3-secrets
@@ -119,6 +121,10 @@
(default 8091))
(gn-doc-git-checkout genenetwork-configuration-gn-doc-git-checkout
(default "/export/data/gn-docs"))
+ (gn-virtuoso-ttl-directory genenetwork-configuration-gn-virtuoso-ttl-directory
+ (default "/export/data/virtuoso/ttl"))
+ (gn-tmpdir genenetwork-configuration-gn-tmpdir
+ (default "/opt/gn/tmp"))
(log-level genenetwork-configuration-log-level
(default 'warning)
(sanitize sanitize-log-level)))
@@ -142,6 +148,8 @@
(default "https://auth.genenetwork.org"))
(gn2-server-url gn-uploader-configuration-gn2-server-url
(default "https://genenetwork.org"))
+ (sessions-dir gn-uploader-sessions-dir
+ (default "/var/genenetwork/sessions/gn-uploader"))
(log-level gn-uploader-configuration-log-level
(default 'warning)
(sanitize sanitize-log-level)))
@@ -170,6 +178,8 @@
(genenetwork3 (genenetwork-configuration-genenetwork3 config))
(xapian-directory (genenetwork-configuration-xapian-db config))
(sparql-endpoint (genenetwork-configuration-sparql-endpoint config))
+ (virtuoso-ttl-directory
+ (genenetwork-configuration-gn-virtuoso-ttl-directory config))
(xapian-build-directory (string-append xapian-directory "/build"))
(herd (file-append shepherd "/bin/herd"))
(index-genenetwork (file-append genenetwork3 "/bin/index-genenetwork"))
@@ -203,7 +213,9 @@
"is-data-modified"
#$xapian-directory
#$sql-uri
- #$sparql-endpoint))))
+ #$sparql-endpoint
+ "--virtuoso-ttl-directory"
+ #$virtuoso-ttl-directory))))
(dynamic-wind
(const #t)
;; build the index
@@ -212,7 +224,9 @@
"create-xapian-index"
#$xapian-build-directory
#$sql-uri
- #$sparql-endpoint)
+ #$sparql-endpoint
+ "--virtuoso-ttl-directory"
+ #$virtuoso-ttl-directory)
(dynamic-wind
;; stop GN3: Here there is magic!!!
;; The name `gunicorn-genenetwork' is magical. It is not set
@@ -242,7 +256,7 @@
(define (genenetwork-activation config)
(match-record config <genenetwork-configuration>
- (gn2-secrets gn3-secrets gn-auth-secrets auth-db llm-db-path)
+ (gn2-secrets gn3-secrets gn-auth-secrets auth-db llm-db-path genotype-files gn-tmpdir gn-doc-git-checkout gn2-sessions-dir)
(with-imported-modules '((guix build utils))
#~(begin
(use-modules (guix build utils))
@@ -262,7 +276,11 @@
(passwd:uid (getpw "gunicorn-genenetwork2"))
(passwd:gid (getpw "gunicorn-genenetwork2"))))
(append (list #$gn2-secrets)
+ (find-files #$genotype-files
+ #:directories? #t)
(find-files #$gn2-secrets
+ #:directories? #t)
+ (find-files #$gn2-sessions-dir
#:directories? #t)))
(for-each (lambda (file)
(chown file
@@ -278,7 +296,31 @@
(find-files #$gn2-secrets
#:directories? #f)
(find-files #$gn-auth-secrets
- #:directories? #f)))))))
+ #:directories? #f)))
+ ;; Make sub-directories for various apps under gn-tmpdir and assign
+ ;; appropriate permissions
+ (for-each (match-lambda
+ ((subdir user)
+ (let ((full-path
+ (string-append #$gn-tmpdir "/" subdir)))
+ (unless (file-exists? full-path)
+ (mkdir full-path #o755))
+ (for-each (lambda (file)
+ (chown file
+ (passwd:uid (getpw user))
+ (passwd:gid (getpw user))))
+ (find-files full-path
+ #:directories? #t)))))
+ '(("gn2-tmpdir" "gunicorn-genenetwork2")
+ ("gn3-tmpdir" "gunicorn-genenetwork3")))
+
+ ;; setup correct ownership for gn-docs
+ (for-each (lambda (file)
+ (chown file
+ (passwd:uid (getpw "genenetwork"))
+ (passwd:gid (getpw "genenetwork"))))
+ (find-files #$gn-doc-git-checkout
+ #:directories? #t))))))
(define (configuration-file-gexp alist)
"Return a G-expression that constructs a configuration file of
@@ -309,7 +351,7 @@ G-expressions or numbers."
described by @var{config}, a @code{<genenetwork-configuration>}
object."
(match-record config <genenetwork-configuration>
- (genenetwork2 genenetwork3 gn-auth server-name gn-auth-server-name gn2-port gn3-port gn-auth-port sql-uri auth-db xapian-db genotype-files sparql-endpoint gn-sourcecode-directory gn3-data-directory gn2-secrets gn3-secrets gn-auth-secrets llm-db-path log-level)
+ (genenetwork2 genenetwork3 gn-auth server-name gn-auth-server-name gn2-port gn3-port gn-auth-port sql-uri auth-db xapian-db genotype-files gn2-sessions-dir sparql-endpoint gn-sourcecode-directory gn3-data-directory gn2-secrets gn3-secrets gn-auth-secrets llm-db-path gn-tmpdir log-level)
;; 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
@@ -333,7 +375,9 @@ object."
("JS_GUIX_PATH" ,(file-append gn2-profile "/share/genenetwork2/javascript"))
("PLINK_COMMAND" ,(file-append gn2-profile "/bin/plink2"))
("SQL_URI" ,sql-uri)
- ("AI_SEARCH_ENABLED" "True")))))
+ ("AI_SEARCH_ENABLED" "True")
+ ("SESSION_FILESYSTEM_CACHE_PATH" ,gn2-sessions-dir)
+ ("MAX_FORM_MEMORY_SIZE" 52428800)))))
(gn3-profile (profile
(content (package->development-manifest genenetwork3))
(allow-collisions? #t)))
@@ -358,9 +402,9 @@ object."
(configuration-file-gexp
`(("GN_AUTH_SECRETS" ,(string-append gn-auth-secrets "/gn-auth-secrets.py"))
("AUTH_DB" ,auth-db)
- ("SQL_URI" ,sql-uri)
- ("CLIENTS_SSL_PUBLIC_KEYS_DIR" ,(string-append gn-auth-secrets "/clients-public-keys"))
- ("SSL_PRIVATE_KEY" ,(string-append gn-auth-secrets "/gn-auth-ssl-private-key.pem")))))))
+ ("SQL_URI" ,sql-uri)))))
+ (gn2-tmpdir (string-append gn-tmpdir "/gn2-tmpdir"))
+ (gn3-tmpdir (string-append gn-tmpdir "/gn3-tmpdir")))
(list (gunicorn-app
(name "genenetwork2")
(package genenetwork2)
@@ -375,7 +419,7 @@ object."
(value gn2-profile))
(environment-variable
(name "TMPDIR")
- (value "/tmp"))
+ (value gn2-tmpdir))
(environment-variable
(name "GN2_SETTINGS")
(value gn2-conf))
@@ -388,13 +432,17 @@ object."
(mappings (list database-mapping
(file-system-mapping
(source genotype-files)
- (target source))
+ (target source)
+ (writable? #t))
(file-system-mapping
(source gn-sourcecode-directory)
(target source))
- (file-system-mapping ; GN2 and GN3 need to share TMPDIR
- (source "/tmp")
- (target "/tmp")
+ (file-system-mapping ; GN2 and GN3 need to communicate via TMPDIR
+ (source gn-tmpdir)
+ (target source))
+ (file-system-mapping
+ (source gn2-tmpdir)
+ (target source)
(writable? #t))
(file-system-mapping
(source gn2-conf)
@@ -408,7 +456,11 @@ object."
(writable? #t))
(file-system-mapping
(source gn2-ca-bundle)
- (target source))))
+ (target source))
+ (file-system-mapping
+ (source gn2-sessions-dir)
+ (target source)
+ (writable? #t))))
(extra-cli-arguments
(list "--log-level"
(string-upcase (symbol->string log-level)))))
@@ -429,7 +481,7 @@ object."
(value gn3-conf))
(environment-variable
(name "TMPDIR")
- (value "/tmp"))
+ (value gn3-tmpdir))
(environment-variable
(name "GN3_SECRETS")
(value gn3-secrets))
@@ -455,9 +507,12 @@ object."
(file-system-mapping
(source gn3-data-directory)
(target source)) ; Rqtl usese this
- (file-system-mapping ; GN2 and GN3 need to share TMPDIR
- (source "/tmp")
- (target "/tmp")
+ (file-system-mapping ; GN2 and GN3 need to communicate via TMPDIR
+ (source gn-tmpdir)
+ (target source))
+ (file-system-mapping
+ (source gn3-tmpdir)
+ (target source)
(writable? #t))
(file-system-mapping
(source xapian-db)
@@ -526,7 +581,8 @@ a @code{<genenetwork-configuration>} record."
(number->string gn2-port) ";")
"proxy_set_header Host $host;"
"proxy_read_timeout 20m;"
- "proxy_set_header X-Forwarded-Proto $scheme;")))
+ "proxy_set_header X-Forwarded-Proto $scheme;"
+ "client_max_body_size 8050m;")))
(nginx-location-configuration
(uri "/api3/")
(body (list "rewrite /api3/(.*) /api/$1 break;"
@@ -628,14 +684,18 @@ a @code{<genenetwork-configuration>} record."
(define (gn-uploader-activation config)
(match-record config <gn-uploader-configuration>
- (secrets data-directory)
+ (secrets data-directory sessions-dir)
(with-imported-modules '((guix build utils))
#~(begin
(use-modules (guix build utils))
;; Let service user own their own secrets files.
- (chown #$secrets
- (passwd:uid (getpw "gunicorn-gn-uploader"))
- (passwd:gid (getpw "gunicorn-gn-uploader")))
+ (for-each (lambda (file)
+ (chown file
+ (passwd:uid (getpw "gunicorn-gn-uploader"))
+ (passwd:gid (getpw "gunicorn-gn-uploader"))))
+ (append (list #$secrets)
+ (find-files #$sessions-dir
+ #:directories? #t)))
;; Set owner-only permissions on secrets files.
(for-each (lambda (file)
(chmod file #o600))
@@ -651,25 +711,26 @@ a @code{<genenetwork-configuration>} record."
(define (gn-uploader-gunicorn-app config)
(match-record config <gn-uploader-configuration>
- (gn-uploader sql-uri port data-directory secrets log-level auth-server-url gn2-server-url)
+ (gn-uploader sql-uri port data-directory secrets log-level auth-server-url gn2-server-url sessions-dir)
;; 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)))
- (gn-uploader-conf (computed-file "gn-uploader.conf"
- (configuration-file-gexp
- `(("UPLOADER_SECRETS" ,secrets)
- ("SQL_URI" ,sql-uri)
- ("UPLOAD_FOLDER" ,(string-append data-directory
- "/uploads"))
- ("AUTH_SERVER_URL" ,auth-server-url)
- ("GN2_SERVER_URL" ,gn2-server-url)))))
- (gn-uploader-profile (profile
- (content (package->development-manifest gn-uploader))
- (allow-collisions? #t)))
- (gn-uploader-ca-bundle (file-append gn-uploader-profile "/etc/ssl/certs/ca-certificates.crt")))
+ (let* ((database-mapping (file-system-mapping
+ (source "/run/mysqld")
+ (target source)
+ (writable? #t)))
+ (gn-uploader-conf (computed-file "gn-uploader.conf"
+ (configuration-file-gexp
+ `(("UPLOADER_SECRETS" ,secrets)
+ ("SQL_URI" ,sql-uri)
+ ("UPLOAD_FOLDER" ,(string-append data-directory
+ "/uploads"))
+ ("AUTH_SERVER_URL" ,auth-server-url)
+ ("GN2_SERVER_URL" ,gn2-server-url)
+ ("SESSION_FILESYSTEM_CACHE_PATH" ,sessions-dir)))))
+ (gn-uploader-profile (profile
+ (content (package->development-manifest gn-uploader))
+ (allow-collisions? #t)))
+ (gn-uploader-ca-bundle (file-append gn-uploader-profile "/etc/ssl/certs/ca-certificates.crt")))
(list (gunicorn-app
(name "gn-uploader")
(package gn-uploader)
@@ -706,7 +767,11 @@ a @code{<genenetwork-configuration>} record."
(target source))
(file-system-mapping
(source gn-uploader-ca-bundle)
- (target source))))
+ (target source))
+ (file-system-mapping
+ (source sessions-dir)
+ (target source)
+ (writable? #t))))
(extra-cli-arguments
(list "--log-level"
(string-upcase (symbol->string log-level)))))))))
diff --git a/production-deploy.sh b/production-deploy.sh
index bd95814..a88fcb8 100755
--- a/production-deploy.sh
+++ b/production-deploy.sh
@@ -29,7 +29,6 @@ container_script=$(guix system container \
--share=/export2/guix-containers/genenetwork/var/genenetwork=/var/genenetwork \
--share=/export2/guix-containers/genenetwork/var/lib/acme=/var/lib/acme \
--share=/export2/guix-containers/genenetwork/var/lib/redis=/var/lib/redis \
- --share=/export/mysql/database=/var/lib/mysql \
--share=/export2/guix-containers/genenetwork/var/lib/virtuoso=/var/lib/virtuoso \
--share=/export2/guix-containers/genenetwork/var/log=/var/log \
--share=/export2/guix-containers/genenetwork/etc/genenetwork=/etc/genenetwork \
@@ -38,7 +37,10 @@ container_script=$(guix system container \
--share=/export2/guix-containers/genenetwork/var/lib/genenetwork-gnqa=/var/lib/genenetwork-gnqa \
--share=/var/run/mysqld=/run/mysqld \
--share=/export/data/gn-docs/ \
- --share=/export2/guix-containers/genenetwork/tmp=/tmp \
+ --share=/export2/guix-containers/genenetwork/tmp=/opt/gn/tmp \
+ --expose=/export2/guix-containers/genenetwork/data/virtuoso=/export/data/virtuoso/ \
+ --share=/export2/guix-containers/genenetwork/var/lib/gn-docs=/export/data/gn-docs \
+ --share=/export2/guix-containers/genenetwork/var/genenetwork/sessions=/var/genenetwork/sessions \
production.scm)
echo $container_script
diff --git a/production.scm b/production.scm
index 933320a..9e629f0 100644
--- a/production.scm
+++ b/production.scm
@@ -44,14 +44,11 @@
"@include " %sudoers-specification
"\nacme ALL = NOPASSWD: " (file-append shepherd "/bin/herd") " restart nginx\n"))
(packages %base-packages)
- (services (cons* (service mysql-service-type
- (mysql-configuration
- (auto-upgrade? #f)))
- (service virtuoso-service-type
+ (services (cons* (service virtuoso-service-type
(virtuoso-configuration
(server-port 9892)
(http-server-port 9893)
- (dirs-allowed "/var/lib/virtuoso")
+ (dirs-allowed (list "/export/data/virtuoso"))
(number-of-buffers 4000000)
(maximum-dirty-buffers 3000000)
(database-file "/var/lib/virtuoso/genenetwork-virtuoso.db")
@@ -80,7 +77,7 @@
(gn3-port 9895)
(gn-auth-port 9896)
(sql-uri
- "mysql://webqtlout:webqtlout@localhost/db_webqtl?unix_socket=/run/mysqld/mysqld.sock")
+ "mysql://webqtlout:webqtlout@localhost/db_webqtl?unix_socket=/run/mysqld/mysqld.sock&charset=utf8")
(xapian-db "/var/lib/xapian")
(sparql-endpoint "http://localhost:9893/sparql")
(gn3-data-directory "/var/genenetwork/data/genenetwork3")
@@ -90,5 +87,6 @@
(auth-db "/var/lib/genenetwork-sqlite/auth.db")
(llm-db-path "/var/lib/genenetwork-gnqa/llm.db")
(gn3-alias-server-port 9800)
+ (gn-tmpdir "/opt/gn/tmp")
(log-level 'debug)))
%base-services)))
diff --git a/public-sparql-deploy.sh b/public-sparql-deploy.sh
index bee9abf..4ecacc6 100755
--- a/public-sparql-deploy.sh
+++ b/public-sparql-deploy.sh
@@ -26,6 +26,7 @@ container_script=$(guix system container \
--share=/export2/guix-containers/public-sparql/tmp=/tmp \
--share=/export2/guix-containers/public-sparql/var/log=/var/log \
--share=/export2/guix-containers/public-sparql/var/lib/acme=/var/lib/acme \
+ --share=/export2/guix-containers/genenetwork/data/virtuoso=/export/data/virtuoso \
public-sparql.scm)
echo $container_script
diff --git a/public-sparql.scm b/public-sparql.scm
index f6efb15..4603cec 100644
--- a/public-sparql.scm
+++ b/public-sparql.scm
@@ -20,6 +20,7 @@
(use-modules (gnu)
(gn services databases)
(gnu services web)
+ ((gnu packages admin) #:select (shepherd))
(forge nginx)
(forge socket))
@@ -50,13 +51,17 @@ SPARQL endpoint is listening on."
(targets (list "/dev/sdX"))))
(file-systems %base-file-systems)
(users %base-user-accounts)
+ (sudoers-file
+ (mixed-text-file "sudoers"
+ "@include " %sudoers-specification
+ "\nacme ALL = NOPASSWD: " (file-append shepherd "/bin/herd") " restart nginx\n"))
(packages %base-packages)
(services (cons* (service virtuoso-service-type
(virtuoso-configuration
(server-port %virtuoso-port)
(http-server-port %sparql-port)
(number-of-buffers 4000000)
- (dirs-allowed "/var/lib/virtuoso")
+ (dirs-allowed (list "/export/data/virtuoso"))
(maximum-dirty-buffers 3000000)
(database-file "/var/lib/virtuoso/public-virtuoso.db")
(transaction-file "/var/lib/virtuoso/public-virtuoso.trx")))
diff --git a/uploader-deploy.sh b/uploader-deploy.sh
index fdbbe0c..415790b 100755
--- a/uploader-deploy.sh
+++ b/uploader-deploy.sh
@@ -41,18 +41,22 @@ container_script=$(guix system container \
--network \
--load-path=. \
--verbosity=3 \
- --share=/export2/guix-containers/genenetwork/uploader/var/genenetwork=/var/genenetwork \
- --share=/export2/guix-containers/genenetwork/uploader/var/lib/acme=/var/lib/acme \
- --share=/export2/guix-containers/genenetwork/uploader/var/lib/mysql=/var/lib/mysql \
- --share=/export2/guix-containers/genenetwork/uploader/var/lib/virtuoso=/var/lib/virtuoso \
- --share=/export2/guix-containers/genenetwork/uploader/var/log=/var/log \
- --share=/export2/guix-containers/genenetwork/uploader/etc/genenetwork=/etc/genenetwork \
- --share=/export/data/uploader/genenetwork-xapian=/export/data/genenetwork-xapian \
- --share=/export/data/uploader/genenetwork-sqlite=/export/data/genenetwork-sqlite \
- --expose=/export/data/uploader/genotype_files=/export/data/genenetwork/genotype_files \
- --expose=/export/data/uploader/genenetwork3 \
- --share=/export/data/uploader/gn-uploader \
+ --share=/export/guix-containers/uploader/var/genenetwork=/var/genenetwork \
+ --share=/export/guix-containers/uploader/var/lib/acme=/var/lib/acme \
+ --share=/export/guix-containers/uploader/var/lib/redis=/var/lib/redis \
+ --share=/export/guix-containers/uploader/var/lib/virtuoso=/var/lib/virtuoso \
+ --share=/export/guix-containers/uploader/var/log=/var/log \
+ --share=/export/guix-containers/uploader/etc/genenetwork=/etc/genenetwork \
+ --share=/export/guix-containers/uploader/var/lib/genenetwork-xapian=/var/lib/xapian \
+ --share=/export/guix-containers/uploader/var/lib/genenetwork-sqlite=/var/lib/genenetwork-sqlite \
+ --share=/export/guix-containers/uploader/var/lib/genenetwork-gnqa=/var/lib/genenetwork-gnqa \
--share=/var/run/mysqld3307=/run/mysqld \
+ --share=/export/data/gn-docs \
+ --share=/export/guix-containers/uploader/tmp=/opt/gn/tmp \
+ --expose=/export/guix-containers/uploader/data/virtuoso=/export/data/virtuoso/ \
+ --share=/export/guix-containers/uploader/var/lib/gn-docs=/export/data/gn-docs \
+ --share=/export/guix-containers/uploader/var/genenetwork/sessions=/var/genenetwork/sessions \
+ --share=/export/data/uploader/gn-uploader \
uploader.scm)
echo "${container_script}"
diff --git a/uploader.scm b/uploader.scm
index 62ab35f..5064426 100644
--- a/uploader.scm
+++ b/uploader.scm
@@ -42,11 +42,7 @@
"@include " %sudoers-specification
"\nacme ALL = NOPASSWD: " (file-append shepherd "/bin/herd") " restart nginx\n"))
(packages %base-packages)
- (services (cons* (service virtuoso-service-type
- (virtuoso-configuration
- (server-port 10892)
- (http-server-port 10893)))
- (service forge-nginx-service-type
+ (services (cons* (service forge-nginx-service-type
(forge-nginx-configuration
(http-listen (forge-ip-socket
(ip "0.0.0.0")
@@ -64,24 +60,26 @@
(gn2-port 10894)
(gn3-port 10895)
(gn-auth-port 10896)
- (sql-uri "mysql://webqtlout:webqtlout@127.0.0.1:3307/db_webqtl")
- (auth-db "/export/data/genenetwork-sqlite/auth.db")
- (xapian-db "/export/data/genenetwork-xapian")
- (genotype-files "/export/data/genenetwork/genotype_files")
+ (sql-uri
+ "mysql://webqtlout:webqtlout@localhost/db_webqtl?unix_socket=/run/mysqld/mysqld.sock&charset=utf8")
+ (auth-db "/var/lib/genenetwork-sqlite/auth.db")
+ (xapian-db "/var/lib/xapian")
(sparql-endpoint "http://localhost:10893/sparql")
- (gn3-data-directory "/export/data/uploader/genenetwork3")
+ (gn3-data-directory "/var/genenetwork/data/genenetwork3")
(gn2-secrets "/etc/genenetwork/genenetwork2")
(gn3-secrets "/etc/genenetwork/genenetwork3/gn3-secrets.py")
- (gn-auth-secrets "/etc/genenetwork/gn-auth")))
+ (gn-auth-secrets "/etc/genenetwork/gn-auth")
+ (llm-db-path "/var/lib/genenetwork-gnqa/llm.db")))
(service gn-uploader-service-type
(gn-uploader-configuration
(gn-uploader gn-uploader)
(server-name "staging-uploader.genenetwork.org")
(port 10897)
(secrets "/etc/genenetwork/gn-uploader/gn-uploader-secrets.py")
- (sql-uri "mysql://webqtlout:webqtlout@127.0.0.1:3307/db_webqtl")
+ (sql-uri
+ "mysql://webqtlout:webqtlout@localhost/db_webqtl?unix_socket=/run/mysqld/mysqld.sock&charset=utf8")
(data-directory "/export/data/uploader/gn-uploader")
- (log-level "DEBUG")
+ (log-level 'debug)
(auth-server-url "https://staging-auth.genenetwork.org/")
(gn2-server-url "https://staging.genenetwork.org")))
%base-services)))
diff --git a/virtuoso-deploy.sh b/virtuoso-deploy.sh
index 0414a65..0dd2509 100755
--- a/virtuoso-deploy.sh
+++ b/virtuoso-deploy.sh
@@ -25,6 +25,7 @@ container_script=$(guix system container \
--network \
--verbosity=3 \
--share=/export2/guix-containers/virtuoso/var/lib/virtuoso=/var/lib/virtuoso \
+ --share=/export2/guix-containers/genenetwork/data/virtuoso=/export/data/virtuoso \
virtuoso.scm)
echo $container_script
diff --git a/virtuoso.scm b/virtuoso.scm
index edcd575..3272f41 100644
--- a/virtuoso.scm
+++ b/virtuoso.scm
@@ -34,5 +34,5 @@
(virtuoso-configuration
(server-port 8891)
(http-server-port 8892)
- (dirs-allowed "/var/lib/virtuoso")))
+ (dirs-allowed (list "/export/data/virtuoso"))))
%base-services)))