unofficial mirror of guix-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Brainstorming ideas for define-configuration
@ 2023-03-09  2:28 Bruno Victal
  2023-03-09  8:13 ` Attila Lendvai
                   ` (5 more replies)
  0 siblings, 6 replies; 9+ messages in thread
From: Bruno Victal @ 2023-03-09  2:28 UTC (permalink / raw)
  To: guix-devel
  Cc: Felix Lechner, mirai, Liliana Marie Prikler, Maxim Cournoyer,
	Ludovic Courtès

Co-authored-by: Felix Lechner


After spending some time with old and new Guix services, I'd like to
suggest some potential improvements to our define-configuration macro:


User-specified sanitizer support
===============================================================================

The sanitizers should be integrated with the type. Otherwise, they are
tedious to use and appear verbose when repeatedly applied to multiple fields.

--8<---------------cut here---------------start------------->8---
;; Suggestion #1
;; The procedure could return a sanitized value. Upon failure, there are
;; the following options:
;;    - The procedure returns only a special value (akin to %unset-value)
;;      and an error message, as a pair.
;;      Exception raising is done by define-sanitized macro behind the scenes
;;      which makes the procedure easier to write.
;;    - The procedure raises an exception. There would be no consistency
;;      on the message formats, however, except for any agreed convention.
;;      This would involve some code duplication.
;; Cons: too specific, not extensible.

(define-sanitized typename procedure
  (prefix ...))


;; Suggestion #2
;; A user-supplied procedure ('procname' below) would work just like the
;; procedure in option #1.
;; There is some similiarity to the Guix record-type*.
;; This could be extended more easily in the future should it be required.
(define-type typename        ; maybe call this 'define-configuration-type' ?
  (sanitizer procname)
  (maybe-type? #t)
  ;; The properties below are service specific.
  ;; If this is implemented with Guix record-type* then we could have a
  ;; module containing generic types and do something along the lines of:
  ;; (define-type foo-ip-address
  ;;    (inherit generic-ip-address)
  ;;    (serializer ...))
  (serializer procname)      ; define-type/no-serialization = sets this field to #f ?
  (prefix ...))
--8<---------------cut here---------------end--------------->8---

The original motivation for this proposal stems from the attempts to
resolve issue #61570. There, one potential fix was to handle the user and
group fields similarly to the way greetd service handles them.
There is some opportunity for generalization here, types that might be
useful elsewhere, such as a port number or a host name, could be placed in
a separate module.

This proposal should also make it easier to handle situations when fields
become obsolete.


Record Validator
===============================================================================

There is also a need to validate records. Matching fields alone do not actually
ensure that the configuration is coherent and usable. For example, some fields
may be mutually incompatible with others.

We could provide procedures that validate each record type within
define-configuration itself instead of validating the value at runtime (i.e.
within the body of the service-type).

--8<---------------cut here---------------start------------->8---
;; the common case
(define-configuration foo-configuration
  (name
   string
   "Lorem ipsum...")

  ;; ...

  (validator procname))

;; [bonus] Simpler configurations that only care for mutually-exclusive fields
(define-configuration foo-configuration
  (name
   string
   "Lorem ipsum...")

  (title
   string
   "Lorem ipsum..."
   (conflicts 'name)))
--8<---------------cut here---------------end--------------->8---


Comments regarding literal placement
-------------------------------------------------------------------------------

Does the placement order matter for the extra fields/literals for define-configuration?
Can they be placed arbitrarily or only at the bottom? (where custom-serializer is specified)

Another point, extra parameters given to define-configuration should never
clash with field names. For example, it should be possible to name a
field 'prefix', 'location' or similar without causing issues like #59423.


Coalesced documentation
===============================================================================

Currently, we manually edit the texinfo output from configuration->documentation
if we're unsatisfied with the generated result.
For instance, substituting @item with an @itemx marker for fields whose
documentation is similar.

Instead, we could embed hints in define-configuration that affect the texinfo
generation in smarter ways.

In the long term, guix.texi should source some portions of the documentation
directly from the code base. The current workflow involving copy and paste
the output from the evaluation of configuration->documentation carries the
unnecessary risk that future documentation patches are done against guix.texi
rather than the .scm file from where it was generated. (issue #60582)

Snippet based on mympd-ip-acl (gnu/services/audio.scm):
--8<---------------cut here---------------start------------->8---
(define-configuration/no-serialization mympd-ip-acl
  (allow
   (list-of-string '())
   "Allowed/Disallowed IP addresses.")

  (deny
   (list-of-string '())
   (documentation 'allow)))  ; group with field 'allow

;;; --- texi output below ---

@c %start of fragment
@deftp {Data Type} mympd-ip-acl
Available @code{mympd-ip-acl} fields are:

@table @asis
@item @code{allow}
@itemx @code{deny} (default: @code{()}) (type: list-of-string)
Allowed/Disallowed IP addresses.

@end table
@end deftp
@c %end of fragment
--8<---------------cut here---------------end--------------->8---


Serializer access to other fields
===============================================================================

Serialization procedures generally only have access to the values of its own field. That
may be insufficient in some cases as whether a field can be serialized
or how that is done, for example, can depend on the value of other fields.

mympd-service-type is one example where each serialized field depends on the value of
another field. Our standard serializer procedures were useless for that case.

As a side note, the 'this-record' macro does not work with
define-configuration which made it impossible to use
(custom-serializer) for the same effect.

Instead, serializer procedures could take additional keyword arguments:
--8<---------------cut here---------------start------------->8---
(define* (serialize-string field-name value (#:key config))
   (let ((baz-value (assoc-ref config 'baz)))
      (string-append baz-value "/" value)
      ...))
--8<---------------cut here---------------end--------------->8---


Inheritable record-type definition
===============================================================================

The openvpn-service pairs in (gnu services vpn) define a special
macro define-split-configuration with the purpose to avoid code duplication
since the service pairs have multiple fields in common.

Perhaps in a similar vein to SRFI-136, we could have:

--8<---------------cut here---------------start------------->8---
(define-configuration openvpn-common-configuration
  (openvpn
    (file-like openvpn)
    "The OpenVPN package.")
  ;; ...
)

(define-configuration openvpn-server-configuration openvpn-common-configuration
  (tls-auth
    (tls-auth-server #f)
    "Add an additional layer of HMAC authentication on top of the TLS control
channel to protect against DoS attacks.")
  ;; ...
)

;;; or through a literal/keyword approach

(define-configuration openvpn-server-configuration
  (tls-auth
    (tls-auth-server #f)
    "Add an additional layer of HMAC authentication on top of the TLS control
channel to protect against DoS attacks.")

  (parent openvpn-common-configuration))

--8<---------------cut here---------------end--------------->8---


Generic serialize-configuration
===============================================================================

The procedure serialize-configuration inherently assumes that the serialized
configuration must be a single string. This assumption needn't always hold, especially
if the service in question is not a shepherd service.

We could possibly make this procedure a bit more "generic", maybe picking some ideas
from SRFI-171 Transducers.

Another improvement to this procedure is to eliminate (or make optional) the second
parameter, the configuration fields. It's unclear what value it brings since one
can't use fields from another configuration type here.
An argument can be made for selectively serializing some of the fields but then
it would be more practical to make this an optional parameter.



PS.
===============================================================================

Kind of a late realization, but couldn't many of the items above be satisfied
by improvements to define-record-type* instead?
(define-record-type* paired with a documentation literal is nearly equivalent to define-configuration,
sans the serialization scaffolding)



Cheers,
Bruno


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2023-03-15 16:27 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-03-09  2:28 Brainstorming ideas for define-configuration Bruno Victal
2023-03-09  8:13 ` Attila Lendvai
2023-03-09 21:05   ` Josselin Poiret
2023-03-09 14:40 ` Joshua Branson
2023-03-09 20:23 ` Liliana Marie Prikler
2023-03-10 14:10 ` Maxim Cournoyer
2023-03-10 20:15 ` jbranso
2023-03-10 22:37   ` Liliana Marie Prikler
2023-03-15 16:27 ` Ludovic Courtès

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).