mirror of
https://codeberg.org/guix/guix.git
synced 2026-04-28 06:34:05 +00:00
services: configuration: Add environment variable serializer.
This patch implements a general API to serialize configuration records to list of pairs representing environment variables. The car of each pair represents the variable name and the cdr the variable value. * gnu/services/configuration/environment-variables.scm: New file. (serialize-string-environment-variable) (serialize-maybe-string-environment-variable) (serialize-boolean-environment-variable) (serialize-maybe-boolean-environment-variable) (serialize-number-environment-variable) (serialize-maybe-number-environment-variable): New variables. (serialize-environment-variables): New variable. * gnu/services/configuration/utils.scm: New file. (uglify-snake-case): New variable. * tests/services/configuration.scm: Add tests for environment serializer. (wrong type for a field): Adjust error location. * doc/guix.texi: Document it. Change-Id: I81a166576f94d3c8f5bf78c82a02183689a3091c Signed-off-by: Liliana Marie Prikler <liliana.prikler@gmail.com>
This commit is contained in:
committed by
Liliana Marie Prikler
parent
2abfd1370f
commit
0b8e838208
@@ -51663,6 +51663,63 @@ phone-number = 0
|
||||
is-married = true
|
||||
@end example
|
||||
|
||||
@subsubsection Serializing to environment variables
|
||||
@cindex environment variables, serialization of configuration records
|
||||
|
||||
There are services which expect their configuration as environment variables.
|
||||
The @code{(gnu services configuration environment-variables)} module provides
|
||||
facilities to serialize configuration records from
|
||||
@code{(gnu services configuration)} to list of pairs representing environment
|
||||
variables.
|
||||
|
||||
For example this configuration record:
|
||||
|
||||
@lisp
|
||||
(define-configuration/no-serialization server
|
||||
(ssh-port
|
||||
(number 22)
|
||||
"The public SSH port of the server.")
|
||||
(fqdn
|
||||
(maybe-string)
|
||||
"The fully qualified domain name of the server.")
|
||||
(active?
|
||||
(boolean #f)
|
||||
"Whether or not the server should be activated."))
|
||||
|
||||
(define my-server
|
||||
(server
|
||||
(ssh-port 20022)
|
||||
(active? #t)))
|
||||
@end lisp
|
||||
|
||||
with this call:
|
||||
|
||||
@lisp
|
||||
(serialize-environment-variables my-server server-fields
|
||||
#:true-value "1"
|
||||
#:false-value "0")
|
||||
@end lisp
|
||||
|
||||
would yield:
|
||||
|
||||
@lisp
|
||||
'(("SSH_PORT" . "20022")
|
||||
("ACTIVE" . "1"))
|
||||
@end lisp
|
||||
|
||||
@anchor{serialize-environment-variables-procedure}
|
||||
@deffn {Procedure} serialize-environment-variables @var{config} @var{fields} @
|
||||
[@var{selection} #f] [@var{negate?} #f] [#:prefix #f] @
|
||||
[#:true-value "true"] [#:false-value "false"]
|
||||
Serializes the fields whose name is included in SELECTION from CONFIG, a
|
||||
configuration from @code{(gnu services configuration)}, and FIELDS, the
|
||||
list of its field records, to a list of pairs. When NEGATE? is #t all services
|
||||
not included in SELECTION will be serialized. Each pair represents an
|
||||
environment variable. The first element of each pair is the variable name, the
|
||||
second is the value. When PREFIX is a string it is prepended to the variable
|
||||
name. TRUE-VALUE and FALSE-VALUE will be used as a representation for
|
||||
respectfully @code{#t} and @code{#f}.
|
||||
@end deffn
|
||||
|
||||
@c *********************************************************************
|
||||
@cindex troubleshooting, Guix System
|
||||
|
||||
143
gnu/services/configuration/environment-variables.scm
Normal file
143
gnu/services/configuration/environment-variables.scm
Normal file
@@ -0,0 +1,143 @@
|
||||
;;; GNU Guix --- Functional package management for GNU
|
||||
;;; Copyright © 2026 Giacomo Leidi <goodoldpaul@autistici.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Guix.
|
||||
;;;
|
||||
;;; GNU Guix 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.
|
||||
;;;
|
||||
;;; GNU Guix 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 GNU Guix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(define-module (gnu services configuration environment-variables)
|
||||
#:use-module (gnu services configuration)
|
||||
#:use-module (gnu services configuration utils)
|
||||
#:use-module (guix diagnostics)
|
||||
#:use-module (guix i18n)
|
||||
#:use-module (ice-9 match)
|
||||
#:use-module (srfi srfi-1)
|
||||
#:export (serialize-string-environment-variable
|
||||
serialize-boolean-environment-variable
|
||||
serialize-number-environment-variable
|
||||
serialize-maybe-string-environment-variable
|
||||
serialize-maybe-boolean-environment-variable
|
||||
serialize-maybe-number-environment-variable
|
||||
|
||||
serialize-environment-variables))
|
||||
|
||||
(define* (field-name->environment-variable field-name
|
||||
#:key prefix
|
||||
(uglify uglify-snake-case))
|
||||
"Serializes FIELD-NAME, a field name from @code{(gnu services configuration)},
|
||||
to an environment variable name through UGLIFY, by default a procedure that is
|
||||
passed FIELD-NAME, and returns a snake case string representation of
|
||||
the field name. Trailing @code{?} in the name are dropped and @code{-} get
|
||||
replaced by @code{_}. When PREFIX is a string, it is prepended to the result.
|
||||
The result of UGLIFY is then upcased and returned.
|
||||
|
||||
For example the procedure would convert @code{'a-field} to @code{\"A_FIELD\"}."
|
||||
(let ((variable (string-upcase
|
||||
(uglify field-name))))
|
||||
(if (string? prefix)
|
||||
(string-append prefix variable)
|
||||
variable)))
|
||||
|
||||
(define* (serialize-string-environment-variable field-name value
|
||||
#:key prefix
|
||||
#:allow-other-keys)
|
||||
(cons (field-name->environment-variable field-name #:prefix prefix)
|
||||
value))
|
||||
|
||||
(define* (serialize-maybe-string-environment-variable field-name value
|
||||
#:key prefix
|
||||
#:allow-other-keys)
|
||||
(if (maybe-value-set? value)
|
||||
(serialize-string-environment-variable field-name value #:prefix prefix)
|
||||
#f))
|
||||
|
||||
(define* (serialize-boolean-environment-variable field-name value
|
||||
#:key prefix
|
||||
(true-value "true")
|
||||
(false-value "false")
|
||||
#:allow-other-keys)
|
||||
(serialize-string-environment-variable
|
||||
field-name (if value true-value false-value)
|
||||
#:prefix prefix))
|
||||
|
||||
(define* (serialize-maybe-boolean-environment-variable field-name value
|
||||
#:key prefix
|
||||
#:allow-other-keys)
|
||||
(if (maybe-value-set? value)
|
||||
(serialize-boolean-environment-variable field-name value #:prefix prefix)
|
||||
#f))
|
||||
|
||||
(define* (serialize-number-environment-variable field-name value
|
||||
#:key prefix
|
||||
#:allow-other-keys)
|
||||
(cons (field-name->environment-variable field-name #:prefix prefix)
|
||||
(number->string value)))
|
||||
|
||||
(define* (serialize-maybe-number-environment-variable field-name value
|
||||
#:key prefix
|
||||
#:allow-other-keys)
|
||||
(if (maybe-value-set? value)
|
||||
(serialize-number-environment-variable field-name value #:prefix prefix)
|
||||
#f))
|
||||
|
||||
(define (environment-variable-serializer field)
|
||||
(define type (configuration-field-type field))
|
||||
(match type
|
||||
('string serialize-string-environment-variable)
|
||||
('maybe-string serialize-maybe-string-environment-variable)
|
||||
('number serialize-number-environment-variable)
|
||||
('integer serialize-number-environment-variable)
|
||||
('positive serialize-number-environment-variable)
|
||||
('maybe-number serialize-maybe-number-environment-variable)
|
||||
('maybe-integer serialize-maybe-number-environment-variable)
|
||||
('maybe-positive serialize-maybe-number-environment-variable)
|
||||
('boolean serialize-boolean-environment-variable)
|
||||
('maybe-boolean serialize-boolean-environment-variable)
|
||||
(_
|
||||
(raise
|
||||
(formatted-message
|
||||
(G_ "Unknown environment-variable field type: ~a")
|
||||
type)))))
|
||||
|
||||
(define* (serialize-environment-variables config fields
|
||||
#:optional selection negate?
|
||||
#:key prefix
|
||||
(true-value "true")
|
||||
(false-value "false"))
|
||||
"Serializes the fields whose name is included in SELECTION from CONFIG, a
|
||||
configuration from @code{(gnu services configuration)}, and FIELDS, the
|
||||
list of its field records, to a list of pairs. When NEGATE? is #t all services
|
||||
not included in SELECTION will be serialized. Each pair represents an
|
||||
environment variable. The first element of each pair is the variable name, the
|
||||
second is the value. When PREFIX is a string it is prepended to the variable
|
||||
name. TRUE-VALUE and FALSE-VALUE will be used as a representation for
|
||||
respectfully @code{#t} and @code{#f}."
|
||||
(define selected-names
|
||||
(or selection
|
||||
(map configuration-field-name fields)))
|
||||
(define filtered
|
||||
(filter-configuration-fields fields selected-names negate?))
|
||||
(define getters
|
||||
(map configuration-field-getter filtered))
|
||||
(define names
|
||||
(map configuration-field-name filtered))
|
||||
(define serializers
|
||||
(map environment-variable-serializer filtered))
|
||||
|
||||
(filter-map (match-lambda ((serializer name getter)
|
||||
(serializer name (getter config)
|
||||
#:prefix prefix
|
||||
#:true-value true-value
|
||||
#:false-value false-value)))
|
||||
(zip serializers names getters)))
|
||||
35
gnu/services/configuration/utils.scm
Normal file
35
gnu/services/configuration/utils.scm
Normal file
@@ -0,0 +1,35 @@
|
||||
;;; GNU Guix --- Functional package management for GNU
|
||||
;;; Copyright © 2026 Giacomo Leidi <goodoldpaul@autistici.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Guix.
|
||||
;;;
|
||||
;;; GNU Guix 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.
|
||||
;;;
|
||||
;;; GNU Guix 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 GNU Guix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(define-module (gnu services configuration utils)
|
||||
#:use-module (ice-9 string-fun)
|
||||
#:export (uglify-snake-case))
|
||||
|
||||
(define (uglify-snake-case field-name)
|
||||
"Serializes FIELD-NAME, a field name from @code{(gnu services configuration)},
|
||||
to a downcased, snake case string representation of the field name. Trailing
|
||||
@code{?} in the name are dropped and dashes are replaced with underscores.
|
||||
|
||||
For example the procedure would convert @code{'A-Field?} to @code{\"a_field\"}."
|
||||
(define str (symbol->string field-name))
|
||||
(string-downcase
|
||||
(string-replace-substring
|
||||
(if (string-suffix? "?" str)
|
||||
(string-drop-right str 1)
|
||||
str)
|
||||
"-" "_")))
|
||||
@@ -3,6 +3,7 @@
|
||||
;;; Copyright © 2021 Xinglu Chen <public@yoctocell.xyz>
|
||||
;;; Copyright © 2022 Ludovic Courtès <ludo@gnu.org>
|
||||
;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
|
||||
;;; Copyright © 2026 Giacomo Leidi <therewasa@fishinthecalculator.me>
|
||||
;;;
|
||||
;;; This file is part of GNU Guix.
|
||||
;;;
|
||||
@@ -21,6 +22,7 @@
|
||||
|
||||
(define-module (tests services configuration)
|
||||
#:use-module (gnu services configuration)
|
||||
#:use-module (gnu services configuration environment-variables)
|
||||
#:use-module (guix diagnostics)
|
||||
#:use-module (guix gexp)
|
||||
#:autoload (guix i18n) (G_)
|
||||
@@ -48,14 +50,14 @@
|
||||
(port-configuration-port (port-configuration)))
|
||||
|
||||
(test-equal "wrong type for a field"
|
||||
'("configuration.scm" 59 11) ;error location
|
||||
'("configuration.scm" 61 11) ;error location
|
||||
(guard (c ((configuration-error? c)
|
||||
(let ((loc (error-location c)))
|
||||
(list (basename (location-file loc))
|
||||
(location-line loc)
|
||||
(location-column loc)))))
|
||||
(port-configuration
|
||||
;; This is line 58; the test relies on line/column numbers!
|
||||
;; This is line 60; the test relies on line/column numbers!
|
||||
(port "This is not a number!"))))
|
||||
|
||||
(define-configuration port-configuration-cs
|
||||
@@ -363,3 +365,126 @@
|
||||
(config-with-maybe-string/no-serialization-name
|
||||
(config-with-maybe-string/no-serialization
|
||||
(name "foo")))))
|
||||
|
||||
|
||||
;;;
|
||||
;;; environment-variables serializer
|
||||
;;;
|
||||
|
||||
(define-configuration/no-serialization env-config
|
||||
(port (number 80) "")
|
||||
(count maybe-number "")
|
||||
(name string "")
|
||||
(url maybe-string "")
|
||||
(active? (boolean #f) ""))
|
||||
|
||||
(test-group "environment variables serializer"
|
||||
|
||||
(test-equal "basic serialization"
|
||||
'(("PORT" . "70")
|
||||
("COUNT" . "80")
|
||||
("NAME" . "Hello World")
|
||||
("URL" . "https://example.org")
|
||||
("ACTIVE" . "true"))
|
||||
(serialize-environment-variables
|
||||
(env-config
|
||||
(port 70)
|
||||
(name "Hello World")
|
||||
(url "https://example.org")
|
||||
(active? #t)
|
||||
(count 80))
|
||||
env-config-fields))
|
||||
|
||||
(test-equal "field selection"
|
||||
'(("PORT" . "80"))
|
||||
(serialize-environment-variables
|
||||
(env-config
|
||||
(name "Hello World"))
|
||||
env-config-fields
|
||||
'(port)))
|
||||
|
||||
(test-equal "field negative selection"
|
||||
'(("NAME" . "Hello World")
|
||||
("ACTIVE" . "false"))
|
||||
(serialize-environment-variables
|
||||
(env-config
|
||||
(name "Hello World"))
|
||||
env-config-fields
|
||||
'(port)
|
||||
#t))
|
||||
|
||||
(test-equal "variable prefixes"
|
||||
'(("TEST_PORT" . "80")
|
||||
("TEST_NAME" . "Hello World")
|
||||
("TEST_ACTIVE" . "false"))
|
||||
(serialize-environment-variables
|
||||
(env-config
|
||||
(name "Hello World"))
|
||||
env-config-fields
|
||||
#:prefix "TEST_"))
|
||||
|
||||
(test-equal "boolean serialization"
|
||||
'(("PORT" . "80")
|
||||
("NAME" . "Hello World")
|
||||
("ACTIVE" . "1"))
|
||||
(serialize-environment-variables
|
||||
(env-config
|
||||
(name "Hello World")
|
||||
(active? #t))
|
||||
env-config-fields
|
||||
#:true-value "1"
|
||||
#:false-value "0"))
|
||||
|
||||
(test-equal "full record serialization"
|
||||
'(("TEST_COUNT" . "800")
|
||||
("TEST_NAME" . "Hello World")
|
||||
("TEST_URL" . "https://example.org")
|
||||
("TEST_ACTIVE" . "1"))
|
||||
(serialize-environment-variables
|
||||
(env-config
|
||||
(port 90)
|
||||
(name "Hello World")
|
||||
(count 800)
|
||||
(url "https://example.org")
|
||||
(active? #t))
|
||||
env-config-fields
|
||||
'(port)
|
||||
#t
|
||||
#:prefix "TEST_"
|
||||
#:true-value "1"
|
||||
#:false-value "0")))
|
||||
|
||||
(define-configuration another-env-config
|
||||
(port
|
||||
(number 80)
|
||||
""
|
||||
(serializer serialize-number-environment-variable))
|
||||
(count
|
||||
(maybe-number)
|
||||
""
|
||||
(serializer serialize-maybe-number-environment-variable))
|
||||
(name
|
||||
(string)
|
||||
""
|
||||
(serializer serialize-string-environment-variable))
|
||||
(url
|
||||
(maybe-string)
|
||||
""
|
||||
(serializer serialize-maybe-string-environment-variable))
|
||||
(active?
|
||||
(boolean #f)
|
||||
""
|
||||
(serializer serialize-boolean-environment-variable)))
|
||||
|
||||
(test-group "environment variables serializer, with field serializers"
|
||||
|
||||
(test-assert "full record serialization"
|
||||
(gexp?
|
||||
(serialize-configuration
|
||||
(another-env-config
|
||||
(port 90)
|
||||
(name "Hello World")
|
||||
(count 800)
|
||||
(url "https://example.org")
|
||||
(active? #t))
|
||||
another-env-config-fields))))
|
||||
|
||||
Reference in New Issue
Block a user