Subject: bug#27155: [PATCH 0/2] Support service extensions on the "final" service values

From: [email-masked] (Ludovic Courtès)
Date: Sat, 03 Jun 2017 23:21:01 +0200

Ludovic Courtès <[email-masked]> skribis:

> This patch adds support for service extensions that modify the
> "final" values of a service.  This is meant to implement cross-cutting
> concerns as well as system-wide customization as discussed with Alex
> long ago:
>
>   https://lists.gnu.org/archive/html/guix-devel/2015-11/msg00623.html
>   https://lists.gnu.org/archive/html/guix-devel/2016-09/msg01505.html
>
> To summarize, a "finalization extension" (for lack of a better name)
> gets the final value of a service and returns a new value for that
> service.

I found a better name: “customizations”.

> For example, for the /etc service, a "normal" extension can only add
> entries for /etc.  A "finalization" extension can instead inspect and
> change all the /etc entries.  IOW, it is a sort of a "sudo" for service
> extensions; it's also quite inelegant compared to the "normal" extension
> mechanism, but it's certainly useful.

Not liking the “sudo” aspect of this patch, I thought it would be
natural if service types could control how customizations apply.  That
way, the PAM or /etc service could still guarantee, for instance, that
customization does not add or remove entries, and so on.

In the end, this control by the service type makes it easier to reason
about what extensions do, whereas the “sudo” style means that an
extension can alter the service’s value in any possible way.

So I started modifying this patch set to add a ‘customize’ field to
<service-type>, next to ‘extend’.  For the PAM and /etc services,
‘customize’ would compose and apply procedures that modify an entry, for
instance.

Then I realized that the only difference between ‘customize’ and
‘extend’ would be the meaning attached to it.  IOW, both are some kind
of an extension.

So at this point, I started wondering whether we should just allow
service types to declare several extension points.  So for PAM, we’d do:

--8<---------------cut here---------------start------------->8---
(define pam-service-addition
  ;; The extension point to add PAM services.
  (service-extension-point
    (compose concatenate)
    (extend append)))

(define pam-service-cutomization
  ;; The extension point to customize PAM services.
  (service-extension-point
    (compose compose)
    (extend append)))

(define pam-root-service-type
  (service-type (name 'pam)
                (extensions (list (service-extension etc-service-type
                                                     /etc-entry)))

                (extension-points (list pam-service-addtion
                                        pam-service-customization))))
--8<---------------cut here---------------end--------------->8---

But then ‘service-extension’ would need to specify not only the target
service type but also the target extension point, which means more
boilerplate, etc.

So after so much thought and hacking, I feel like the ad hoc solution at
<https://lists.gnu.org/archive/html/guix-devel/2016-09/msg01505.html>
was not that bad after all.

Sorry to bother you with philosophical design questions when we already
have two ways to solve the problem at hand, but I feel like there’s a
pattern worth looking for!

Ludo’.