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
|
is-married = true
|
||||||
@end example
|
@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 *********************************************************************
|
@c *********************************************************************
|
||||||
@cindex troubleshooting, Guix System
|
@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 © 2021 Xinglu Chen <public@yoctocell.xyz>
|
||||||
;;; Copyright © 2022 Ludovic Courtès <ludo@gnu.org>
|
;;; Copyright © 2022 Ludovic Courtès <ludo@gnu.org>
|
||||||
;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
|
;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
|
||||||
|
;;; Copyright © 2026 Giacomo Leidi <therewasa@fishinthecalculator.me>
|
||||||
;;;
|
;;;
|
||||||
;;; This file is part of GNU Guix.
|
;;; This file is part of GNU Guix.
|
||||||
;;;
|
;;;
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
|
|
||||||
(define-module (tests services configuration)
|
(define-module (tests services configuration)
|
||||||
#:use-module (gnu services configuration)
|
#:use-module (gnu services configuration)
|
||||||
|
#:use-module (gnu services configuration environment-variables)
|
||||||
#:use-module (guix diagnostics)
|
#:use-module (guix diagnostics)
|
||||||
#:use-module (guix gexp)
|
#:use-module (guix gexp)
|
||||||
#:autoload (guix i18n) (G_)
|
#:autoload (guix i18n) (G_)
|
||||||
@@ -48,14 +50,14 @@
|
|||||||
(port-configuration-port (port-configuration)))
|
(port-configuration-port (port-configuration)))
|
||||||
|
|
||||||
(test-equal "wrong type for a field"
|
(test-equal "wrong type for a field"
|
||||||
'("configuration.scm" 59 11) ;error location
|
'("configuration.scm" 61 11) ;error location
|
||||||
(guard (c ((configuration-error? c)
|
(guard (c ((configuration-error? c)
|
||||||
(let ((loc (error-location c)))
|
(let ((loc (error-location c)))
|
||||||
(list (basename (location-file loc))
|
(list (basename (location-file loc))
|
||||||
(location-line loc)
|
(location-line loc)
|
||||||
(location-column loc)))))
|
(location-column loc)))))
|
||||||
(port-configuration
|
(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!"))))
|
(port "This is not a number!"))))
|
||||||
|
|
||||||
(define-configuration port-configuration-cs
|
(define-configuration port-configuration-cs
|
||||||
@@ -363,3 +365,126 @@
|
|||||||
(config-with-maybe-string/no-serialization-name
|
(config-with-maybe-string/no-serialization-name
|
||||||
(config-with-maybe-string/no-serialization
|
(config-with-maybe-string/no-serialization
|
||||||
(name "foo")))))
|
(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