diff options
| author | Giacomo Leidi <therewasa@fishinthecalculator.me> | 2026-01-10 17:23:44 +0100 |
|---|---|---|
| committer | Giacomo Leidi <therewasa@fishinthecalculator.me> | 2026-03-09 23:17:24 +0100 |
| commit | 520785e315eddbe47199ac557e88e60eca3ae97c (patch) | |
| tree | 5af120ab54fdc523729e13cde06d13f197358710 /gnu | |
| parent | 1a109f379835315c0a4ea23b1ff52d0e77d7fede (diff) | |
gnu: Add soju-service-type.
* gnu/services/messaging.scm (%default-soju-shepherd-requirement): New
variable.
(soju-ssl-certificate): New configuration record.
(soju-database): New configuration record.
(soju-configuration): New configuration record.
(serialize-soju-configuration,soju-activation,soju-accounts,
soju-shepherd-services): New procedures.
(soju-service-type): New service.
(serialize-ngircd-configuration): Reformat.
(pounce-configuration): Reformat.
* doc/guix.texi: Document the new soju service.
* gnu/tests/messaging.scm: Test the new soju service.
Change-Id: I6223ecac1aaaab76bd75461851ffe4cec0678118
Diffstat (limited to 'gnu')
| -rw-r--r-- | gnu/services/messaging.scm | 346 | ||||
| -rw-r--r-- | gnu/tests/messaging.scm | 112 |
2 files changed, 454 insertions, 4 deletions
diff --git a/gnu/services/messaging.scm b/gnu/services/messaging.scm index 1fbb1f1d857..ab89338969c 100644 --- a/gnu/services/messaging.scm +++ b/gnu/services/messaging.scm | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | ;;; Copyright © 2018 Pierre-Antoine Rouby <contact@parouby.fr> | 5 | ;;; Copyright © 2018 Pierre-Antoine Rouby <contact@parouby.fr> |
| 6 | ;;; Copyright © 2025 Maxim Cournoyer <maxim@guixotic.coop> | 6 | ;;; Copyright © 2025 Maxim Cournoyer <maxim@guixotic.coop> |
| 7 | ;;; Copyright © 2024 Evgeny Pisemsky <mail@pisemsky.site> | 7 | ;;; Copyright © 2024 Evgeny Pisemsky <mail@pisemsky.site> |
| 8 | ;;; Copyright © 2026 Giacomo Leidi <therewasa@fishinthecalculator.me> | ||
| 8 | ;;; | 9 | ;;; |
| 9 | ;;; This file is part of GNU Guix. | 10 | ;;; This file is part of GNU Guix. |
| 10 | ;;; | 11 | ;;; |
| @@ -30,12 +31,15 @@ | |||
| 30 | #:autoload (gnu packages rust-apps) (mollysocket) | 31 | #:autoload (gnu packages rust-apps) (mollysocket) |
| 31 | #:use-module (gnu packages tls) | 32 | #:use-module (gnu packages tls) |
| 32 | #:use-module (gnu services) | 33 | #:use-module (gnu services) |
| 34 | #:use-module (gnu services admin) | ||
| 33 | #:use-module (gnu services shepherd) | 35 | #:use-module (gnu services shepherd) |
| 34 | #:use-module (gnu services configuration) | 36 | #:use-module (gnu services configuration) |
| 35 | #:use-module (gnu system shadow) | 37 | #:use-module (gnu system shadow) |
| 36 | #:autoload (gnu build linux-container) (%namespaces) | 38 | #:autoload (gnu build linux-container) (%namespaces) |
| 37 | #:use-module ((gnu system file-systems) #:select (file-system-mapping)) | 39 | #:use-module ((gnu system file-systems) #:select (file-system-mapping)) |
| 40 | #:use-module (guix diagnostics) | ||
| 38 | #:use-module (guix gexp) | 41 | #:use-module (guix gexp) |
| 42 | #:use-module (guix i18n) | ||
| 39 | #:use-module (guix modules) | 43 | #:use-module (guix modules) |
| 40 | #:use-module (guix records) | 44 | #:use-module (guix records) |
| 41 | #:use-module (guix packages) | 45 | #:use-module (guix packages) |
| @@ -208,7 +212,43 @@ | |||
| 208 | mollysocket-configuration-allowed-uuids | 212 | mollysocket-configuration-allowed-uuids |
| 209 | mollysocket-configuration-db | 213 | mollysocket-configuration-db |
| 210 | mollysocket-configuration-vapid-key-file | 214 | mollysocket-configuration-vapid-key-file |
| 211 | mollysocket-service-type)) | 215 | mollysocket-service-type |
| 216 | |||
| 217 | %default-soju-shepherd-requirement | ||
| 218 | |||
| 219 | soju-ssl | ||
| 220 | soju-ssl? | ||
| 221 | soju-ssl-fields | ||
| 222 | soju-ssl-certificate | ||
| 223 | soju-ssl-key | ||
| 224 | |||
| 225 | soju-database | ||
| 226 | soju-database? | ||
| 227 | soju-database-fields | ||
| 228 | soju-database-datadir | ||
| 229 | soju-database-driver | ||
| 230 | soju-database-source | ||
| 231 | |||
| 232 | soju-configuration | ||
| 233 | soju-configuration? | ||
| 234 | soju-configuration-fields | ||
| 235 | soju-configuration-soju | ||
| 236 | soju-configuration-debug? | ||
| 237 | soju-configuration-listen | ||
| 238 | soju-configuration-hostname | ||
| 239 | soju-configuration-title | ||
| 240 | soju-configuration-ssl-certificate | ||
| 241 | soju-configuration-log-file | ||
| 242 | soju-configuration-shepherd-requirement | ||
| 243 | soju-configuration-database | ||
| 244 | soju-configuration-extra-content | ||
| 245 | |||
| 246 | soju-configuration->mixed-text-file | ||
| 247 | soju-activation | ||
| 248 | soju-accounts | ||
| 249 | soju-shepherd-services | ||
| 250 | |||
| 251 | soju-service-type)) | ||
| 212 | 252 | ||
| 213 | ;;; Commentary: | 253 | ;;; Commentary: |
| 214 | ;;; | 254 | ;;; |
| @@ -1570,7 +1610,7 @@ values." | |||
| 1570 | ;; Ensure stdin is not connected to a TTY source to avoid ngircd | 1610 | ;; Ensure stdin is not connected to a TTY source to avoid ngircd |
| 1571 | ;; configtest blocking with a confirmation prompt. | 1611 | ;; configtest blocking with a confirmation prompt. |
| 1572 | (parameterize ((current-input-port (%make-void-port "r"))) | 1612 | (parameterize ((current-input-port (%make-void-port "r"))) |
| 1573 | (invoke #+ngircd "--config" #$ngircd.conf "--configtest" )) | 1613 | (invoke #+ngircd "--config" #$ngircd.conf "--configtest")) |
| 1574 | (copy-file #$ngircd.conf #$output)))))) | 1614 | (copy-file #$ngircd.conf #$output)))))) |
| 1575 | 1615 | ||
| 1576 | (define (ngircd-wrapper config) | 1616 | (define (ngircd-wrapper config) |
| @@ -1804,7 +1844,7 @@ reconnecting client. The size must be a power of two.") | |||
| 1804 | maybe-string | 1844 | maybe-string |
| 1805 | "Host to bind the @emph{source} address to when connecting to the server. | 1845 | "Host to bind the @emph{source} address to when connecting to the server. |
| 1806 | To connect from any address over IPv4 only, use @samp{0.0.0.0}. To connect | 1846 | To connect from any address over IPv4 only, use @samp{0.0.0.0}. To connect |
| 1807 | from any address over IPv6 only, use @samp{::}." ) | 1847 | from any address over IPv6 only, use @samp{::}.") |
| 1808 | 1848 | ||
| 1809 | (host | 1849 | (host |
| 1810 | string | 1850 | string |
| @@ -2388,3 +2428,303 @@ if it does not exist.") | |||
| 2388 | mollysocket-activation-service))) | 2428 | mollysocket-activation-service))) |
| 2389 | (default-value (mollysocket-configuration)) | 2429 | (default-value (mollysocket-configuration)) |
| 2390 | (description "UnifiedPush provider for the Signal client Molly."))) | 2430 | (description "UnifiedPush provider for the Signal client Molly."))) |
| 2431 | |||
| 2432 | |||
| 2433 | ;;; | ||
| 2434 | ;;; Soju | ||
| 2435 | ;;; | ||
| 2436 | |||
| 2437 | (define %default-soju-shepherd-requirement | ||
| 2438 | '(user-processes loopback)) | ||
| 2439 | |||
| 2440 | (define-configuration soju-ssl | ||
| 2441 | (certificate | ||
| 2442 | (string) | ||
| 2443 | "Where to find the certificate for secure connections." | ||
| 2444 | (serializer empty-serializer)) | ||
| 2445 | (key | ||
| 2446 | (string) | ||
| 2447 | "Where to find the private key for secure connections." | ||
| 2448 | (serializer empty-serializer))) | ||
| 2449 | |||
| 2450 | (define (soju-serialize-soju-ssl name value) | ||
| 2451 | #~(string-append "tls " | ||
| 2452 | #$(soju-ssl-certificate value) | ||
| 2453 | " " | ||
| 2454 | #$(soju-ssl-key value) | ||
| 2455 | "\n")) | ||
| 2456 | |||
| 2457 | (define-maybe soju-ssl (prefix soju-)) | ||
| 2458 | |||
| 2459 | (define (soju-sanitize-db-driver value) | ||
| 2460 | (if (or (eq? value 'sqlite3) | ||
| 2461 | (eq? value 'postgres)) | ||
| 2462 | value | ||
| 2463 | (raise | ||
| 2464 | (formatted-message | ||
| 2465 | (G_ "db-driver can be either 'sqlite3 or 'postgres but ~a was found") | ||
| 2466 | value)))) | ||
| 2467 | |||
| 2468 | (define (soju-serialize-symbol name value) (symbol->string value)) | ||
| 2469 | |||
| 2470 | (define (soju-serialize-string name value) | ||
| 2471 | (define quoted-value | ||
| 2472 | #~(if (string-contains #$value " ") | ||
| 2473 | (string-append "\"" #$value "\"") | ||
| 2474 | #$value)) | ||
| 2475 | #~(string-append (symbol->string '#$name) " " #$quoted-value "\n")) | ||
| 2476 | |||
| 2477 | (define soju-serialize-package serialize-package) | ||
| 2478 | |||
| 2479 | (define-maybe string (prefix soju-)) | ||
| 2480 | |||
| 2481 | (define-configuration soju-database | ||
| 2482 | (datadir | ||
| 2483 | (string "/var/lib/soju") | ||
| 2484 | "The name of the directory where soju will write its state.") | ||
| 2485 | (driver | ||
| 2486 | (symbol 'sqlite3) | ||
| 2487 | "Set the database driver for user, network and channel storage. | ||
| 2488 | |||
| 2489 | Supported drivers: | ||
| 2490 | |||
| 2491 | @itemize | ||
| 2492 | @item @code{'sqlite3} | ||
| 2493 | @item @code{'postgres} | ||
| 2494 | @end itemize" | ||
| 2495 | (sanitizer soju-sanitize-db-driver) | ||
| 2496 | (serializer soju-serialize-symbol)) | ||
| 2497 | (source | ||
| 2498 | (maybe-string) | ||
| 2499 | "Set the database location for user, network and channel storage. By | ||
| 2500 | default, a sqlite3 database is opened in the directory specified in the | ||
| 2501 | @code{datadir} field. | ||
| 2502 | |||
| 2503 | In general the driver expect the following: | ||
| 2504 | |||
| 2505 | @itemize | ||
| 2506 | @item @code{'sqlite3} expects source to be a path to the SQLite file | ||
| 2507 | @item @code{'postgres} expects source to be a space-separated list of | ||
| 2508 | @code{key=value} parameters, e.g. @code{\"host=/run/postgresql dbname=soju\"}. | ||
| 2509 | Note that @code{sslmode defaults} to @code{require}. For more information on | ||
| 2510 | connection strings, see | ||
| 2511 | @url{https://pkg.go.dev/github.com/lib/pq#hdr-Connection_String_Parameters, | ||
| 2512 | upstream}'s documentation. | ||
| 2513 | @end itemize" | ||
| 2514 | (serializer soju-serialize-maybe-string))) | ||
| 2515 | |||
| 2516 | (define (soju-db-source driver source datadir) | ||
| 2517 | (if (not (string=? "" source)) | ||
| 2518 | source | ||
| 2519 | (if (string=? driver "sqlite3") | ||
| 2520 | (string-append datadir "/soju.db") | ||
| 2521 | (raise | ||
| 2522 | (G_ "db-source can't be empty when db-driver is set to 'postgres. | ||
| 2523 | Make sure to pass a connection string"))))) | ||
| 2524 | |||
| 2525 | (define (soju-serialize-soju-database name config) | ||
| 2526 | (define fields | ||
| 2527 | (filter-configuration-fields | ||
| 2528 | soju-database-fields | ||
| 2529 | '(driver source))) | ||
| 2530 | (define getters | ||
| 2531 | (map configuration-field-getter fields)) | ||
| 2532 | (define names | ||
| 2533 | (map configuration-field-name fields)) | ||
| 2534 | (define serializers | ||
| 2535 | (map configuration-field-serializer fields)) | ||
| 2536 | (define values | ||
| 2537 | (map (match-lambda ((serializer name getter) | ||
| 2538 | (serializer name (getter config)))) | ||
| 2539 | (zip serializers names getters))) | ||
| 2540 | |||
| 2541 | #~(string-append "db " #$(first values) | ||
| 2542 | " " #$(soju-db-source | ||
| 2543 | (first values) | ||
| 2544 | (second values) | ||
| 2545 | (soju-database-datadir config)) | ||
| 2546 | "\n")) | ||
| 2547 | |||
| 2548 | (define (soju-serialize-list-of-strings name value) | ||
| 2549 | #~(string-append | ||
| 2550 | (string-join (map (lambda (l) (string-append "listen " l)) | ||
| 2551 | (list #$@value)) | ||
| 2552 | "\n") | ||
| 2553 | "\n")) | ||
| 2554 | |||
| 2555 | |||
| 2556 | (define soju-serialize-text-config serialize-text-config) | ||
| 2557 | |||
| 2558 | (define-configuration soju-configuration | ||
| 2559 | (soju | ||
| 2560 | (package soju) | ||
| 2561 | "Soju package to use for the service." | ||
| 2562 | (serializer empty-serializer)) | ||
| 2563 | (debug? | ||
| 2564 | (boolean #f) | ||
| 2565 | "Enable debug logging (this will leak sensitive information such as | ||
| 2566 | passwords). This can be overriden at run time with the service command | ||
| 2567 | @code{server debug}." | ||
| 2568 | (serializer empty-serializer)) | ||
| 2569 | (listen | ||
| 2570 | (list-of-strings '(":6697")) | ||
| 2571 | "Listening URI. The following URIs are supported: | ||
| 2572 | |||
| 2573 | @itemize | ||
| 2574 | |||
| 2575 | @item @code{[ircs://][host][:port]} listens with TLS over TCP (default port if | ||
| 2576 | omitted: 6697) | ||
| 2577 | @item @code{irc://localhost[:port]} listens with plain-text over TCP (default | ||
| 2578 | port if omitted: 6667, host must be @code{\"localhost\"}) | ||
| 2579 | @item @code{irc+insecure://[host][:port]} listens with plain-text over TCP | ||
| 2580 | (default port if omitted: 6667) | ||
| 2581 | @item @code{unix://<path>} listens on a Unix domain socket | ||
| 2582 | @item @code{https://[host][:port]} listens for HTTPS connections (default port: | ||
| 2583 | 443) and handles the following requests: @code{/socket} for WebSocket and | ||
| 2584 | @code{/uploads} (and subdirectories) for file uploads | ||
| 2585 | @item @code{http://localhost[:port]} listens for plain-text HTTP connections | ||
| 2586 | (default port: 80, host must be @code{\"localhost\"}) and handles requests like | ||
| 2587 | @code{https://} does | ||
| 2588 | @item @code{http+insecure://[host][:port]} listens for plain-text HTTP | ||
| 2589 | connections (default port: 80) and handles requests like @code{https://} does | ||
| 2590 | @item @code{http+unix://<path>} listens for plain-text HTTP connections on a | ||
| 2591 | Unix domain socket and handles requests like @code{https://} does | ||
| 2592 | @item @code{wss://[host][:port]} listens for WebSocket connections over TLS | ||
| 2593 | (default port: 443) | ||
| 2594 | @item @code{ws://localhost[:port]} listens for plain-text WebSocket connections | ||
| 2595 | (default port: 80, host must be @code{\"localhost\"}) | ||
| 2596 | @item @code{ws+insecure://[host][:port]} listens for plain-text WebSocket | ||
| 2597 | connections (default port: 80) | ||
| 2598 | @item @code{ws+unix://<path>} listens for plain-text WebSocket connections on a | ||
| 2599 | Unix domain socket | ||
| 2600 | @item @code{ident://[host][:port]} listens for plain-text ident connections | ||
| 2601 | (default port: 113) | ||
| 2602 | @item @code{http+prometheus://localhost:<port>} listens for plain-text HTTP | ||
| 2603 | connections and serves Prometheus metrics (host must be @code{\"localhost\"}) | ||
| 2604 | @item @code{http+pprof://localhost:<port>} listens for plain-text HTTP | ||
| 2605 | connections and serves pprof runtime profiling data (host must be | ||
| 2606 | @code{\"localhost\"}). For more information, see | ||
| 2607 | @url{https://pkg.go.dev/net/http/pprof,upstream} documentation | ||
| 2608 | @item @code{unix+admin://[path]} listens on a Unix domain socket for | ||
| 2609 | administrative connections, such as sojuctl (default path: | ||
| 2610 | @code{/run/soju/admin}) | ||
| 2611 | |||
| 2612 | @end itemize | ||
| 2613 | |||
| 2614 | If the scheme is omitted, @code{ircs} is assumed. If multiple @code{listen} | ||
| 2615 | values are specified, soju will listen on each of them.") | ||
| 2616 | (hostname | ||
| 2617 | (maybe-string) | ||
| 2618 | "Server hostname, it defaults to the system hostname. This should be set to | ||
| 2619 | a fully qualified domain name.") | ||
| 2620 | (title | ||
| 2621 | (string) | ||
| 2622 | "Server title. This will be sent as the @code{ISUPPORT NETWORK} value when | ||
| 2623 | clients don't select a specific network.") | ||
| 2624 | (ssl-certificate | ||
| 2625 | (maybe-soju-ssl) | ||
| 2626 | "Where to find the private key for secure connections. If set, this field | ||
| 2627 | will have the service run under root privileges.") | ||
| 2628 | (shepherd-requirement | ||
| 2629 | (list %default-soju-shepherd-requirement) | ||
| 2630 | "A list of Shepherd services to use. Add extra dependencies to | ||
| 2631 | @code{%default-soju-shepherd-requirement} to extend its value." | ||
| 2632 | (serializer empty-serializer)) | ||
| 2633 | (log-file | ||
| 2634 | (string "/var/log/soju.log") | ||
| 2635 | "The name of the file where soju will write its logs." | ||
| 2636 | (serializer empty-serializer)) | ||
| 2637 | (db | ||
| 2638 | (soju-database (soju-database)) | ||
| 2639 | "The database where soju will write its state.") | ||
| 2640 | (extra-content | ||
| 2641 | (text-config '()) | ||
| 2642 | "Extra content to append to the configuration as-is.") | ||
| 2643 | (prefix soju-)) | ||
| 2644 | |||
| 2645 | (define (soju-needs-privileges? config) | ||
| 2646 | ;; NOTE: Certificates on the Guix System are only readable by root. In case a | ||
| 2647 | ;; certificate and a private key are passed no unprivileged users will be | ||
| 2648 | ;; added to the system. | ||
| 2649 | (maybe-value-set? | ||
| 2650 | (soju-configuration-ssl-certificate config))) | ||
| 2651 | |||
| 2652 | (define (serialize-soju-configuration config) | ||
| 2653 | (mixed-text-file "soju.conf" | ||
| 2654 | (serialize-configuration | ||
| 2655 | config | ||
| 2656 | (filter-configuration-fields | ||
| 2657 | soju-configuration-fields | ||
| 2658 | '(soju debug? shepherd-requirement log-file) | ||
| 2659 | #t)))) | ||
| 2660 | |||
| 2661 | (define (soju-activation config) | ||
| 2662 | (let ((datadir (soju-database-datadir | ||
| 2663 | (soju-configuration-db config))) | ||
| 2664 | (user-name | ||
| 2665 | (if (soju-needs-privileges? config) "root" "soju"))) | ||
| 2666 | #~(let* ((user (getpwnam #$user-name)) | ||
| 2667 | (uid (passwd:uid user)) | ||
| 2668 | (gid (passwd:gid user)) | ||
| 2669 | (datadir #$datadir)) | ||
| 2670 | ;; Setup datadir | ||
| 2671 | (mkdir-p datadir) | ||
| 2672 | (chown datadir uid gid) | ||
| 2673 | (for-each (lambda (f) (chown f uid gid)) | ||
| 2674 | (find-files datadir ".*"))))) | ||
| 2675 | |||
| 2676 | (define (soju-accounts config) | ||
| 2677 | (if (soju-needs-privileges? config) | ||
| 2678 | '() | ||
| 2679 | (list (user-group (name "soju") (system? #t)) | ||
| 2680 | (user-account | ||
| 2681 | (name "soju") | ||
| 2682 | (group "soju") | ||
| 2683 | (system? #t) | ||
| 2684 | (comment "soju server user") | ||
| 2685 | (home-directory "/var/empty") | ||
| 2686 | (shell (file-append shadow "/sbin/nologin")))))) | ||
| 2687 | |||
| 2688 | (define (soju-shepherd-services config) | ||
| 2689 | (match-record config <soju-configuration> | ||
| 2690 | (soju debug? log-file shepherd-requirement) | ||
| 2691 | (let ((user-name (if (soju-needs-privileges? config) "root" "soju")) | ||
| 2692 | (soju-binary (file-append soju "/bin/soju")) | ||
| 2693 | (config-file (serialize-soju-configuration config))) | ||
| 2694 | (list (shepherd-service | ||
| 2695 | (provision '(soju)) | ||
| 2696 | (documentation "Run the soju daemon.") | ||
| 2697 | (requirement shepherd-requirement) | ||
| 2698 | (start #~(make-forkexec-constructor | ||
| 2699 | (list #$soju-binary | ||
| 2700 | #$@(if debug? '("-debug") '()) | ||
| 2701 | "-config" #$config-file) | ||
| 2702 | #:log-file #$log-file | ||
| 2703 | #:user #$user-name | ||
| 2704 | #:group #$user-name)) | ||
| 2705 | (stop #~(make-kill-destructor)) | ||
| 2706 | (actions | ||
| 2707 | (list | ||
| 2708 | (shepherd-action | ||
| 2709 | (name 'reload) | ||
| 2710 | (documentation "Reload soju configuration file and restart | ||
| 2711 | it. It is useful for situations where the same soju configuration file can | ||
| 2712 | point to different things after a reload, such as renewed TLS certificates.") | ||
| 2713 | (procedure | ||
| 2714 | #~(lambda (process . args) | ||
| 2715 | (kill (process-id process) SIGHUP)))) | ||
| 2716 | (shepherd-configuration-action config-file)))))))) | ||
| 2717 | |||
| 2718 | (define soju-service-type | ||
| 2719 | (service-type (name 'soju) | ||
| 2720 | (extensions | ||
| 2721 | (list (service-extension shepherd-root-service-type | ||
| 2722 | soju-shepherd-services) | ||
| 2723 | (service-extension log-rotation-service-type | ||
| 2724 | (compose | ||
| 2725 | list soju-configuration-log-file)) | ||
| 2726 | (service-extension activation-service-type | ||
| 2727 | soju-activation) | ||
| 2728 | (service-extension account-service-type | ||
| 2729 | soju-accounts))) | ||
| 2730 | (description "Run the soju IRC bouncer."))) | ||
diff --git a/gnu/tests/messaging.scm b/gnu/tests/messaging.scm index 83ccca88918..559dc81a747 100644 --- a/gnu/tests/messaging.scm +++ b/gnu/tests/messaging.scm | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | ;;; Copyright © 2018 Efraim Flashner <efraim@flashner.co.il> | 4 | ;;; Copyright © 2018 Efraim Flashner <efraim@flashner.co.il> |
| 5 | ;;; Copyright © 2025 Maxim Cournoyer <maxim@guixotic.coop> | 5 | ;;; Copyright © 2025 Maxim Cournoyer <maxim@guixotic.coop> |
| 6 | ;;; Copyright © 2025 Evgeny Pisemsky <mail@pisemsky.site> | 6 | ;;; Copyright © 2025 Evgeny Pisemsky <mail@pisemsky.site> |
| 7 | ;;; Copyright © 2026 Giacomo Leidi <therewasa@fishinthecalculator.me> | ||
| 7 | ;;; | 8 | ;;; |
| 8 | ;;; This file is part of GNU Guix. | 9 | ;;; This file is part of GNU Guix. |
| 9 | ;;; | 10 | ;;; |
| @@ -44,7 +45,8 @@ | |||
| 44 | %test-ngircd | 45 | %test-ngircd |
| 45 | %test-pounce | 46 | %test-pounce |
| 46 | %test-quassel | 47 | %test-quassel |
| 47 | %test-mosquitto)) | 48 | %test-mosquitto |
| 49 | %test-soju)) | ||
| 48 | 50 | ||
| 49 | (define (run-xmpp-test name xmpp-service pid-file create-account) | 51 | (define (run-xmpp-test name xmpp-service pid-file create-account) |
| 50 | "Run a test of an OS running XMPP-SERVICE, which writes its PID to PID-FILE." | 52 | "Run a test of an OS running XMPP-SERVICE, which writes its PID to PID-FILE." |
| @@ -669,3 +671,111 @@ OPENSSL:localhost:7000,verify=0 &") | |||
| 669 | (name "mosquitto") | 671 | (name "mosquitto") |
| 670 | (description "Test a running Mosquitto MQTT broker.") | 672 | (description "Test a running Mosquitto MQTT broker.") |
| 671 | (value (run-mosquitto-test)))) | 673 | (value (run-mosquitto-test)))) |
| 674 | |||
| 675 | |||
| 676 | ;;; | ||
| 677 | ;;; soju. | ||
| 678 | ;;; | ||
| 679 | |||
| 680 | (define %soju-os | ||
| 681 | (operating-system | ||
| 682 | (inherit %simple-os) | ||
| 683 | (packages (cons* soju %base-packages)) | ||
| 684 | (services | ||
| 685 | (cons* | ||
| 686 | (service dhcpcd-service-type) | ||
| 687 | (service soju-service-type | ||
| 688 | (soju-configuration | ||
| 689 | (debug? #t) | ||
| 690 | (listen '("irc://localhost" "unix+admin:///var/lib/soju/soju.sock")) | ||
| 691 | (title "soju IRC bouncer") | ||
| 692 | (extra-content | ||
| 693 | (list (plain-file "soju.conf" "hostname test.example.org"))))) | ||
| 694 | %base-services)))) | ||
| 695 | |||
| 696 | (define (run-soju-test) | ||
| 697 | (define vm | ||
| 698 | (virtual-machine | ||
| 699 | (operating-system | ||
| 700 | (marionette-operating-system | ||
| 701 | %soju-os | ||
| 702 | #:imported-modules (source-module-closure | ||
| 703 | '((gnu services herd))))))) | ||
| 704 | |||
| 705 | (define test | ||
| 706 | (with-imported-modules '((gnu build marionette)) | ||
| 707 | #~(begin | ||
| 708 | (use-modules (srfi srfi-64) | ||
| 709 | (gnu build marionette)) | ||
| 710 | |||
| 711 | (define marionette | ||
| 712 | (make-marionette (list #$vm))) | ||
| 713 | |||
| 714 | (test-runner-current (system-test-runner #$output)) | ||
| 715 | (test-begin "soju") | ||
| 716 | |||
| 717 | (marionette-eval | ||
| 718 | '(begin | ||
| 719 | (use-modules (gnu services herd)) | ||
| 720 | (wait-for-service 'user-processes)) | ||
| 721 | marionette) | ||
| 722 | |||
| 723 | (test-equal "soju configuration file is well formed" | ||
| 724 | "listen irc://localhost | ||
| 725 | listen unix+admin:///var/lib/soju/soju.sock | ||
| 726 | title \"soju IRC bouncer\" | ||
| 727 | db sqlite3 /var/lib/soju/soju.db | ||
| 728 | hostname test.example.org | ||
| 729 | " | ||
| 730 | (marionette-eval | ||
| 731 | '(begin | ||
| 732 | (use-modules (ice-9 popen) | ||
| 733 | (ice-9 rdelim) | ||
| 734 | (ice-9 textual-ports)) | ||
| 735 | (let* ((port (open-input-pipe "herd configuration soju")) | ||
| 736 | (soju.conf (string-trim-both (read-line port)))) | ||
| 737 | (close-pipe port) | ||
| 738 | (call-with-input-file soju.conf get-string-all))) | ||
| 739 | marionette)) | ||
| 740 | |||
| 741 | (test-assert "soju service runs" | ||
| 742 | (marionette-eval | ||
| 743 | '(begin | ||
| 744 | (use-modules (gnu services herd)) | ||
| 745 | (wait-for-service 'soju)) | ||
| 746 | marionette)) | ||
| 747 | |||
| 748 | (test-assert "soju listens on TCP port 6667" | ||
| 749 | (wait-for-tcp-port 6667 marionette)) | ||
| 750 | |||
| 751 | (test-equal "sojuctl can create a user" | ||
| 752 | "fishinthecalculator (admin): 0 networks" | ||
| 753 | (marionette-eval | ||
| 754 | '(begin | ||
| 755 | (use-modules (ice-9 popen) | ||
| 756 | (ice-9 rdelim)) | ||
| 757 | (system (string-join | ||
| 758 | '("sojuctl" "-config" "$(herd configuration soju)" | ||
| 759 | "user" "create" "-admin" | ||
| 760 | "-username" "fishinthecalculator" | ||
| 761 | "-password" "1234") | ||
| 762 | " ")) | ||
| 763 | (let* ((port (open-input-pipe (string-join | ||
| 764 | '("sojuctl" "-config" | ||
| 765 | "$(herd configuration soju)" | ||
| 766 | "user" "status") | ||
| 767 | " "))) | ||
| 768 | (msg (read-line port))) | ||
| 769 | (close-pipe port) | ||
| 770 | msg)) | ||
| 771 | marionette)) | ||
| 772 | |||
| 773 | (test-end)))) | ||
| 774 | |||
| 775 | (gexp->derivation "soju-test" test)) | ||
| 776 | |||
| 777 | (define %test-soju | ||
| 778 | (system-test | ||
| 779 | (name "soju") | ||
| 780 | (description "Run a soju IRC bouncer.") | ||
| 781 | (value (run-soju-test)))) | ||
