guix_mirror_bot pushed a commit to branch master
in repository guix.
commit ce8df300216b308720903e6da9e01d246143b47f
Author: wrobell <[email protected]>
AuthorDate: Tue Jun 16 20:17:41 2026 +0100
services: Add epmd-service-type.
Add Erlang Port Mapper Daemon (epmd) service.
Run the service on localhost by default.
* gnu/services/high-availability.scm (<epmd-configuration>): New record.
(epmd-service-type): New variable.
* gnu/tests/high-availability.scm (run-epmd-test): New procedure.
(%epmd-os, %tests-epmd): New variables.
* doc/gnu.texi (High Availability Services): Document it.
Merges: https://codeberg.org/guix/guix/pulls/9607
References: https://www.rabbitmq.com/blog/2024/12/18/epmd-public-exposure
References: https://erlef.org/blog/eef/epmd-public-exposure
Signed-off-by: Nguyễn Gia Phong <[email protected]>
---
doc/guix.texi | 54 ++++++++++++++++++++++---
gnu/services/high-availability.scm | 81 ++++++++++++++++++++++++++++++++++----
gnu/tests/high-availability.scm | 81 +++++++++++++++++++++++++++++++++++++-
3 files changed, 203 insertions(+), 13 deletions(-)
diff --git a/doc/guix.texi b/doc/guix.texi
index 0db590aa5f..12eca80ed3 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -37746,16 +37746,60 @@ Extra configuration parameters as a list of strings.
@node High Availability Services
@subsection High Availability Services
-@cindex Message broker
-@cindex Message streaming
-The @code{(gnu services high-availability)} module provides RabbitMQ
-message broker service.
+The @code{(gnu services high-availability)} module provides services for
+Erlang Port Mapper Daemon (epmd) and RabbitMQ broker.
+
+@anchor{epmd}
+@subsubheading Erlang Port Mapper Daemon (epmd)
+
+@defvar epmd-service-type
+Service type for the
+@uref{https://www.erlang.org/doc/apps/erts/epmd_cmd.html,Erlang Port Mapper
+Daemon (epmd)}.
+The value for this service type is a @code{<epmd-configuration>} record.
+
+Below is an example configuration:
+
+@lisp
+(service epmd-service-type)
+@end lisp
+@end defvar
+
+@c %start of fragment
+
+@deftp {Data Type} epmd-configuration
+Available @code{epmd-configuration} fields are:
+
+@table @asis
+@item @code{user} (default: @code{"epmd"}) (type: string)
+The user running epmd.
+
+@item @code{group} (default: @code{"epmd"}) (type: string)
+The user group running epmd.
+
+@item @code{address} (default: @code{("127.0.0.1" "::1")}) (type:
list-of-strings)
+List of addresses for epmd to listen on. Set it to false (@code{#f}) to
+listen on all available addresses. Note that epmd cannot start on
+@code{"0.0.0.0"} address.
+
+@item @code{port} (default: @code{4369}) (type: integer)
+Default port for epmd to listen on.
+
+@end table
+
+@end deftp
+
+
+@c %end of fragment
@anchor{RabbitMQ}
@subsubheading RabbitMQ
+@cindex Message broker
+@cindex Message streaming
+
@defvar rabbitmq-service-type
-Service type for the @uref{https://www.rabbitmq.com/,RabbitMQ} web server.
+Service type for the @uref{https://www.rabbitmq.com/,RabbitMQ} server.
The value for this service type is a @code{<rabbitmq-configuration>} record.
A simple example configuration is given below.
diff --git a/gnu/services/high-availability.scm
b/gnu/services/high-availability.scm
index 73d534cf02..8b928e7616 100644
--- a/gnu/services/high-availability.scm
+++ b/gnu/services/high-availability.scm
@@ -20,8 +20,10 @@
(define-module (gnu services high-availability)
#:use-module (gnu packages admin)
+ #:use-module (gnu packages erlang)
#:use-module (gnu packages high-availability)
#:use-module (gnu services)
+ #:use-module (gnu services configuration)
#:use-module (gnu services shepherd)
#:use-module (gnu system shadow)
#:use-module (guix gexp)
@@ -30,13 +32,78 @@
#:use-module (ice-9 match)
#:use-module (ice-9 format)
- #:export (rabbitmq-configuration rabbitmq-configuration?
- rabbitmq-configuration-rabbitmq
- rabbitmq-configuration-node-name
- rabbitmq-configuration-config-file
- rabbitmq-configuration-env-config-file
- rabbitmq-configuration-plugins
- rabbitmq-service-type))
+ #:export (epmd-configuration
+ epmd-configuration-fields
+ epmd-configuration?
+ epmd-configuration-user
+ epmd-configuration-group
+ epmd-configuration-address
+ epmd-configuration-port
+ epmd-service-type
+
+ rabbitmq-configuration
+ rabbitmq-configuration?
+ rabbitmq-configuration-rabbitmq
+ rabbitmq-configuration-node-name
+ rabbitmq-configuration-config-file
+ rabbitmq-configuration-env-config-file
+ rabbitmq-configuration-plugins
+ rabbitmq-service-type))
+
+(define-configuration/no-serialization epmd-configuration
+ (user (string "epmd") "The user running epmd.")
+ (group (string "epmd") "The user group running epmd.")
+ ;; The address default works for IPv4-only or IPv6-only systems. For example,
+ ;; epmd shows warning for ::1 on IPv4-only system, but starts anyway.
+ (address (list-of-strings (list "127.0.0.1" "::1"))
+ "List of addresses for epmd to listen on. Set it to false
+(@code{#f}) to listen on all available addresses. Note that epmd cannot start
+on @code{\"0.0.0.0\"} address.")
+ (port (integer 4369) "Default port for epmd to listen on."))
+
+(define (epmd-accounts config)
+ (match-record config <epmd-configuration> (user group)
+ (list (user-group (name group)
+ (system? #t))
+ (user-account (name user)
+ (group group)
+ (system? #t)
+ (comment "Erlang Port Mapper Daemon user")
+ (home-directory "/var/empty")
+ (shell (file-append shadow "/sbin/nologin"))))))
+
+(define (epmd-shepherd-service config)
+ (match-record config <epmd-configuration>
+ (address port)
+ (let ((address-list (if address (string-join address ",") #f)))
+ (with-imported-modules
+ (source-module-closure '((gnu build shepherd)))
+ (list
+ (shepherd-service
+ (provision '(epmd))
+ (documentation "Run the Erlang Port Mapper Daemon (epmd).")
+ (requirement '(user-processes loopback))
+ (modules '((gnu build shepherd)))
+ (start
+ #~(make-forkexec-constructor
+ (append (list #$(file-append erlang "/bin/epmd")
+ "-port"
+ #$(number->string port))
+ (if #$address-list
+ (list "-address" #$address-list)
+ (list)))
+ #:user "epmd"
+ #:group "epmd"))
+ (stop #~(make-kill-destructor))))))))
+
+(define epmd-service-type
+ (service-type
+ (name 'epmd)
+ (description "Run the Erlang Port Mapper Daemon (epmd).")
+ (extensions (list (service-extension shepherd-root-service-type
+ epmd-shepherd-service)
+ (service-extension account-service-type epmd-accounts)))
+ (default-value (epmd-configuration))))
;; By default, start messaging and inter-node RabbitMQ listeners on local
;; interfaces only, see also:
diff --git a/gnu/tests/high-availability.scm b/gnu/tests/high-availability.scm
index 6eb13d1a06..faa657bfc3 100644
--- a/gnu/tests/high-availability.scm
+++ b/gnu/tests/high-availability.scm
@@ -19,6 +19,7 @@
(define-module (gnu tests high-availability)
#:use-module (gnu tests)
+ #:use-module (gnu packages erlang)
#:use-module (gnu packages high-availability)
#:use-module (gnu system)
#:use-module (gnu system file-systems)
@@ -29,7 +30,85 @@
#:use-module (gnu services networking)
#:use-module (guix gexp)
#:use-module (guix store)
- #:export (%test-rabbitmq))
+ #:export (%test-epmd
+ %test-rabbitmq))
+
+;;;
+;;; Erlang Port Mapper Daemon (epmd) service.
+;;;
+(define %epmd-os
+ (simple-operating-system
+ (service epmd-service-type
+ (epmd-configuration (port 14369)))))
+
+(define* (run-epmd-test #:key (epmd-port 14369))
+ "Run tests in %EPMD-OS, forwarding PORT."
+ (define os
+ (marionette-operating-system
+ %epmd-os
+ #:imported-modules '((gnu services herd)
+ (guix combinators))))
+
+ (define forwarded-port 14369)
+
+ (define vm
+ (virtual-machine
+ (operating-system os)
+ (memory-size 512)
+ (port-forwardings `((,epmd-port . ,forwarded-port)))))
+
+ (define test
+ (with-imported-modules '((gnu build marionette))
+ #~(begin
+ (use-modules (srfi srfi-64)
+ (gnu build marionette)
+ (ice-9 rdelim))
+
+ (define marionette
+ (make-marionette (list #$vm)))
+
+ (mkdir #$output)
+ (chdir #$output)
+
+ (test-runner-current (system-test-runner #$output))
+ (test-begin "epmd")
+
+ ;; Wait for epmd to be up and running.
+ (test-assert "service running"
+ (marionette-eval
+ '(begin
+ (use-modules (gnu services herd))
+ (match (start-service 'epmd)
+ (#f #f)
+ (('service response-parts ...)
+ (match (assq-ref response-parts 'running)
+ ((#t) #t)
+ ((pid) pid)))))
+ marionette))
+
+ (test-assert "epmd port ready"
+ (wait-for-tcp-port #$forwarded-port marionette))
+
+ (test-assert "epmd connection is successful"
+ (marionette-eval
+ '(begin
+ (use-modules (guix build utils))
+
+ (current-output-port (open-file "/dev/console" "w0"))
+ (invoke #$(file-append erlang "/bin/epmd")
+ "-port"
+ "14369"
+ "-names"))
+ marionette))
+ (test-end))))
+
+ (gexp->derivation "epmd-test" test))
+
+(define %test-epmd
+ (system-test
+ (name "epmd")
+ (description "Connect to a running epmd service.")
+ (value (run-epmd-test))))
(define %rabbitmq-config-file
(plain-file "rabbitmq.conf" "