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" "

Reply via email to