On 2021-12-22 09:53, Xinglu Chen wrote:
Toggle quote (53 lines)
> Am Dienstag, der 21. Dezember 2021, um 13:21 +032, schrieb Andrew Tropin <andrew@trop.in>:
>
>> * guix.texi (Writing Service Configuration): New section.
>> ---
>> After reading the source code of different system services and implementing a
>> few of home services I decided to write down most important tips for
>> implementing guix service configurations. I belive having such a guideline
>> can simplify the development of new services and configurations for them, as
>> well as keeping those implementations consistent, which will simplify the life
>> for users too because they won't need to learn a different configuration
>> approaches for different services.
>>
>> This section is not a final document, but a starting point for discussion and
>> further extension of the guideline. Feel free to raise a question, point to a
>> mistake, make a suggestion or propose an idea.
>
> Thanks for working on this! I left some comments and thoughts as I read
> through it (Warning, these is quite a lot :-)).
>
>> doc/guix.texi | 209 +++++++++++++++++++++++++++++++++++++++++++++++++-
>> 1 file changed, 205 insertions(+), 4 deletions(-)
>>
>> diff --git a/doc/guix.texi b/doc/guix.texi
>> index 333cb4117a..a48fb0e2b7 100644
>> --- a/doc/guix.texi
>> +++ b/doc/guix.texi
>> @@ -35652,10 +35652,11 @@ them in an @code{operating-system} declaration. But how do we define
>> them in the first place? And what is a service anyway?
>>
>> @menu
>> -* Service Composition:: The model for composing services.
>> -* Service Types and Services:: Types and services.
>> -* Service Reference:: API reference.
>> -* Shepherd Services:: A particular type of service.
>> +* Service Composition:: The model for composing services.
>> +* Service Types and Services:: Types and services.
>> +* Service Reference:: API reference.
>> +* Shepherd Services:: A particular type of service.
>> +* Writing Service Configurations:: A guideline for writing guix services.
>> @end menu
>>
>> @node Service Composition
>> @@ -35851,6 +35852,206 @@ There can be only one instance of an extensible service type such as
>> Still here? The next section provides a reference of the programming
>> interface for services.
>>
>> +@node Writing Service Configurations
>> +@subsection Writing Service Configurations
>
> The TOC menu says that “Writing Services Configurations” comes after
> “Shepherd Services”, but this doesn’t seem to be the case here.
>
Done.
Toggle quote (22 lines)
>> +There are a lot of system and home services already written, but from
>> +time to time it's necessary to write one more.
>
> I would write something like
>
> Guix already contains a wide variety of system and home services, but
> sometimes users might want to add new services.
>
>> +This section contains
>> +tips for simplifying this process, and should help to make service
>> +configurations and their implementations more consistent.
>> +
>> +@quotation Note
>> +If you find any exceptions or patterns missing in this section, please
>> +send a patch with additions/changes to @email{guix-devel@@gnu.org}
>> +mailing list or just start a discussion/ask a question.
>> +@end quotation
>
> I don’t think this note is really necessary; there is already a section
> on contributing to the project, see “17 Contributing”.
>
Not necessary, but I would keep it for a few months to make people more
involved in the polishing of this guide.
Toggle quote (7 lines)
>> +@subsubheading Configuration Itself
>> +
>> +As we know from previous section a guix service can accept a value and
> ^ missing comma
> s/section/sections/
> s/guix/Guix
Done.
Toggle quote (4 lines)
>
> When you say “service”, you mean a “service type”, right? Just “value”
> sounds a bit vague, maybe
I mean service, which is instantiated from some service type.
Toggle quote (4 lines)
>
> … a value, usually some kind of configuration
> record (@pxref{RELEVANT NODE(s)})
changed it to service value and added this note.
Toggle quote (11 lines)
>
> ?
>
>> +be extended with additional values by other services.
>
> Not all services are extendable though, to avoid ambiguity, maybe
>
> …, and optionally, be extended with additional configurations by other
> services (@pxref{Service Composition}).
>
Done.
Toggle quote (23 lines)
>> +There are some
>> +cases, when the service accepts a list of pairs or some other values for
>
> I suggest:
>
> When being extended, most services take some kind of configuration
> record or a list thereof, but in some cases a simpler value is all
> that is necessary.
>
>> +example @code{console-font-service-type} accepts list of pairs (tty and
>> +font name/file) or @code{etc-service-type} accepts list of lists
>> +(resulting file name and file-like object)
>
> It is probably better to link to the service documentation instead of
> trying to explain the specification in a few words in brackets. You can
> use Texinfo “anchors” to achieve this, see “5.8 '@anchor': Defining
> Arbitrary Cross-reference Targets”.
>
> For example, @code{console-font-service-type}
> (@pxref{console-font-service-type}) accepts an association list, and
> @code{etc-service-type} (@pxref{etc-service-type}) accepts a list of
> lists.
Slightly rewrote this paragraph. I don't know how to reference index
entries (if it possible at all), so I added anchors for them.
Toggle quote (4 lines)
>
> Also, is should there be any preference for using alists or list of
> lists or vice versa?
Now it should be clear that a -configuration record is preferable as a
service value, lists and alists are special cases for auxiliray
services and shouldn't be used in most cases.
Toggle quote (13 lines)
>
>> +those services are kinda special, they are an intermediate helpers
>> +doing auxiliary work.
>
> It is not clear what the last clause means, how do they differ from
> other, more “regular” services?
>
>> +However, in most cases a guix service is wrapping some software, which
>> +consist of package or a few packages, and configuration file or files.
>
> “…consists of one or more packages and configuration files.”
>
Done.
Toggle quote (7 lines)
>> +Therefore, the value for such service is quite complicated and it's hard
>> +to represent it with a just list or basic data type, in such cases we
>> +use a record. Each such record have -configuration suffix, for example
> ^^ Link to the “Records” page in the Guile manual
>
> @code{-configuration} or maybe @samp{-configuration}
Done.
Toggle quote (11 lines)
>
>> +@code{docker-configuration} for @code{docker-service-type} and a few
>> +different fields helping to customize the software.
>
> I suggest:
>
> …, for example, the @code{docker-service-type} should accept a record
> type named @code{docker-configuration}, which contains a fields used
> to configure Docker.
>
Done.
Toggle quote (4 lines)
>> +Configuration
>> +records for home services also have a @code{home-} prefix in their name.
> ^ missing “should”
Done.
Toggle quote (17 lines)
>
>> +There is a module @code{gnu service configuration}, which contains
>> +helpers simplifying configuration definition process. Take a look at
>> +@code{gnu services docker} module or grep for
>> +@code{define-configuration} to find usage examples.
>> +
>> +@c Provide some examples, tips, and rationale behind @code{gnu service
>> +@c configuration} module.
>
> Note that I already sent a patch that (at least tries to) document (gnu
> service configuration)[1].
>
> One thing that is lacking is when to use (guix records) (which isn’t
> documented yet) vs (gnu service configuration). There should probably
> be one or two paragraphs about that.
>
Saw it, I'll try to review and comment on it, when I'll get some spare
time. I'll keep this comment for now, and after the section about gnu
service configuration module is merged, we will add links to it and
provide more info and examples on implementing actual configurations.
Toggle quote (4 lines)
>
>> +After a configuration record properly named and defined let's discuss
> ^ “…has been…”
Done.
Toggle quote (4 lines)
>
>> +how to name and define fields, and which approach to use for
> ^ missing “the”
Done.
Toggle quote (10 lines)
>
>> +implementing the serialization code related to them.
>
> “serialization” doesn’t seem to be mentioned anywhere else in the manual
> in the context of Guix services, so I think we should avoid using that
> term before explaining what it actually means. Maybe
>
> …and what approach to use to convert Scheme records into strings, which
> will be put into one or more configuration files.
Added a parapgraph about serialization.
Toggle quote (14 lines)
>
>> +@subsubheading Configuration Record Fields
>> +
>> +@enumerate
>> +@item
>> +It's a good idea to have a field/fields for specifying package/packages
>> +being installed for this service. For example
> ^ missing comma
> I suggest
>
> It's a good idea to have one or more fields for specifying the package
> or packages that will be installed by a service.
>
Done.
Toggle quote (5 lines)
>> +@code{docker-configuration} has @code{docker}, @code{docker-cli},
>> +@code{containerd} fields.
>
> Having a link to the docker service would probably be a good idea.
Done.
Toggle quote (11 lines)
>
>> +Sometimes it make sense to make a field,
>> +which accepts a list of packages for cases, where an arbitrary list of
>> +plugins can be passed to the configuration. There are some services,
>> +which provide a field called @code{package} in their configuration,
>> +which is ok, but the way it done in @code{docker-configuration} is more
>> +flexible and thus preferable.
>
> In what way is it more flexible? Just naming the field ‘docker’ would
> be a bit ambigous; ‘docker-package’ make things more clear.
More flexible comparing to just one package field, because it makes it
easier to define a configuration for software requiring a few packages
like docker.
`docker-package` is a good idea, which makes it very clear what the
content of this field should be, however just `docker` should be enough
we already have a type information for this field in documentation and
this pattern is already applied in a few dozens of different services.
Toggle quote (19 lines)
>
>> +@item
>> +Fields for configuration files, should be called the same as target
>
> s/called/named/
>
> “…same as the name of the target configuration file”
>
>> +configuration file name, but in kebab-case: bashrc for bashrc,
>
> Not everyone might familiar with what exactly “kebab-case” means; we
> should probably leave a footnote or something.
>
> “…@code{bashrc} for @file{.bashrc}…”
>
> It should also mention that preceding dots should be removed as well.
> What should happend with files named ‘file.ext’? Should the field be
> named ‘file-ext’?
Added a footnote, provided more expressive examples
@code{bashrc} for @file{.bashrc},
@code{bash-profile} for @file{.bash_profile},
@code{tmux-conf} for @file{tmux.conf}, etc.
Toggle quote (11 lines)
>
>> +bash-profile for bash_profile, etc. The implementation for such fields
>
> “…@code{bash-profile} for @file{.bash_profile}.
>
> Also, many services have an ‘extra-content’, ‘extra-config’, or
> ‘extra-options’ field. In most cases these just take a string and
> appends it to some configuration file. Should these instead be named
> ‘sshd_config’, ‘xserver-conf’, and ‘asound-config’, respectively?
>
I find this pattern purely-established (content vs conf vs options),
unclear (you can never know where this extra content will be inserted
until you take a look at implementation of serialization function) and
uneccesary (we do not need extra-* fields because we can add any
extra content using G-expression inside our primary configuration, see
sway example below).
Toggle quote (11 lines)
>
>> +@item
>> +Other fields in most cases add some boilerplates/reasonable defaults to
> ^ missing “should” maybe?
>
>> +configuration files
>
> Do you mean that for some services, there could be a
> ‘reasonable-defaults?’ field that sets some resonable defaults?
>
Kind of. See guix-default? for home-bash-service-type. Also, fields like
aliases and environment-variables are of the same category I describe here.
Added them as an example of such fields.
Toggle quote (7 lines)
>
>> +turns on/off installation of some packages or provide other custom
>> behavior.
>
> “turns on/off” sounds a bit weird; I think “enable/disabled” sounds
> better.
Done.
Toggle quote (10 lines)
>
>> +There is no any special requirements or
>> +recommendations here, but it's necessary to make it possible to disable
>> +all the effects of such fields to provide a user with an empty
>> +configuration and let them generate it from scratch with only field for
>> +configuration file.
>
> I don’t really understand what is meant by “let them generate it from
> scratch with only field for configuration file”.
The good examples of the bad behavior are alsa and nginx service types,
they always provide some boilerplate with reasonably good default
configuration, but you can't alter it by setting some fields to #f or
some other values.
For nginx it's only partially true, you actually can use `file` field,
but it will alter the effect of all other fields and will just use the
file as nginx.conf, kinda conforms what I'm asking here, but makes all
other fields useless.
Added the following explanation to this item:
Toggle snippet (7 lines)
For example, setting @code{guix-defaults?} to
@code{#f} and @code{aliases} to @code{'()} will give user an ability to
control the content of @file{.bashrc} solely by setting the value of
@code{bashrc} field.
Toggle quote (13 lines)
>
> It doesn’t mention if a configuration record should cover all the
> configuration options available in a configuration file. For example,
> the current ‘openssh-configuration’ has quite a few options, but these
> obviously don’t cover all the options available in /etc/ssh/sshd_config,
> which is why there is an “escape hatch”, ‘extra-content’ field.
>
> In some cases a program might have too many configuration fields for us
> to map using configuration records, e.g., Git. In rde, the approach we
> took was to use nested lists to represent the INI configuration. I
> think this approach could also be mentioned here.
>
This is mentioned below, as well as the problem of closed-world
assumption. Software should be fully configurable with field for
respective config file, escape hatch should be a part of this field.
Escape hatch is necessary to allow to reuse already existing
configuration, but not to provide configuration, which can't be
expressed by respective configuration field.
Toggle quote (12 lines)
>> +@end enumerate
>> +
>> +@subsubheading Fields for Configuration Files
>> +
>> +The field should accept a datastructure (preferably a combination of
> ^ missing space
>> +simple lists, alists, vectors, gexps and basic data types), which will
>
> There should probably be links to at least ‘vectors’ and ‘gexps’, since
> many people probably aren’t too familiar with them.
>
Done.
Toggle quote (7 lines)
>
>> +be serialized to target configuration format, in other words it should
> missing comma ^
>> +provide an alternative lisp syntax, which can be later translated to
>
> Capitalize “lisp”.
Done
Toggle quote (8 lines)
>
>> +target one, like SXML for XML. Such approach is quite flexible and
> ^ missing “a”
>
> You mean “SXML to XML”, right (SXML being the Lisp syntax, and XML being
> the target one)?
>
Yep.
Done.
Toggle quote (4 lines)
>
>> +simple, it requires to write serializer once for one configuration
> ^ “one” or “you”
Sounds better for me without one or you.
Toggle quote (5 lines)
>
>> +format and can be reused multiple times in different guix services.
>
> Capitalize “guix”.
Done.
Toggle quote (10 lines)
>
>> +Let's take a look at JSON: we implement serialization function, which
>> +converts vectors to arrays, alists to objects (AKA dictionaries or
>> +associative arrays), numbers to numbers, gexps to the strings, file-like
>> +objects to the strings, which contains the path to the file in the
>> +store, @code{#t} to @code{true} and so on, and now we have all apps
>
> “Apps” sounds kind of smartphone-y; “programs” is probably more
> appropriate.
Agree.
Toggle quote (4 lines)
>
> There should be a link “file-like object” since it may be unknown for
> many.
Done.
Toggle quote (5 lines)
>
>> +using JSON and YAML as a format for configurations covered. Maybe some
>
> You only mentioned JSON above; why would YAML also be covered by JSON?
JSON is a subset of YAML, so having a serializer for JSON makes it
possible to generate configurations for many YAML-flavored applications.
However, it maybe not that clear and important. Will remove it.
Toggle quote (6 lines)
>
>> +fine-tunning will be needed for particular application, but the primary
>> +serilalization part is already finished.
>
> “serialization” typo.
Done
Toggle quote (7 lines)
>
>> +The pros and cons of such approach is inherited from open-world
>> +assumption. It doesn't matter if underlying applications provides new
> ^ “the”
>
> What do you mean by “open-world assumption”?
They often used when designing programming languages or DSLs.
Toggle quote (4 lines)
>
>> +configuration options, we don't need to change anything in service
> ^ “the”
Done.
Toggle quote (4 lines)
>
>> +configuration and its serialization code, it will work perfectly fine,
> A full stop should probably be used here ^
Done.
Toggle quote (4 lines)
>
>> +on the other hand it harder to type check and structure check
> ^ “is”
Done.
Toggle quote (4 lines)
>> +``compile-time'', and we can end up with configuration, which won't
>> be
> ^ missing “during” or “at”? ^ “a”
Done
Toggle quote (7 lines)
>
>> +accepted by target application cause of unexisting, misspelled or
> ^ “the”
>
> s/application/program/ :-)
> s/cause/because/
Changed `cause of` to `due to`.
Toggle quote (14 lines)
>
>> +wrongly-typed options. It's possible to add those checks, but we will
>> +get the drawbacks of closed-world assumption: we need to keep the
>> +service implementation in-sync with app config options, and it will make
>> +impossible to use the same service with older/newer package version,
>> +which has a slightly different list of available options and will add an
>> +excessive maintanence load.
>> +
>> +However, for some applications with really stable configuration those
>> +checks can be helpful and should be implemented if possible, for some
>> +other we can implement them only partially.
>
> s/other/others/
Done.
Toggle quote (11 lines)
>
>> +The alternative approach applied in some exitsting services is to use
>> +records for defining the structure of configuration field, it has the
>> +same downsides of closed-world assumption and a few more problems:
>> +
>> +@enumerate
>> +@item
>> +It has to replicate all the available options for the app (sometimes
>> +hundreds or thousands) to allow user express any configuration they
> ^ “the”
Done.
Toggle quote (5 lines)
>
>> +wants.
>
> s/wants/want/
Done.
Toggle quote (6 lines)
>
>
>> +@item
>> +Having a few records, adds one more layer of abstraction between service
> ^ spurious comma
Done.
Toggle quote (12 lines)
>
>
>> +configuration and resulting app config, including different field
>> +casing, new semantic units.
>
> But it means that the syntax for configuring a program is more
> Scheme-like. For example, the Dovecot service provides a very
> complicated but Schemeish interface for configuring Dovecot, though as
> you have mentioned, it might be missing some fields since the Dovecot
> configuration file might have changed since the creation of the service.
>
Yes it is more Scheme-flavored, but it doesn't mean good. I can write a
good rationale on this topic, but will do it next time, now I'll just
give you an example, which should be relevant to you: Imagine writing an
importer (for `guix home import` for example) from XML to SXML, now
imagine that instead of SXML we have Scheme-
This message was truncated. Download the full message here.
-----BEGIN PGP SIGNATURE-----
iQJDBAEBCgAtFiEEKEGaxlA4dEDH6S/6IgjSCVjB3rAFAmHEdrMPHGFuZHJld0B0
cm9wLmluAAoJECII0glYwd6w8GoP/jwhRwC4Obv2cBdLhu3xz/qr7If4nrygnh/U
S2AnTu8TeRTbIcoYDCi286gH4xQ230bNNvedkvMcX+cjj/gg4zU6Fy9H9s0ubwbn
Dzep0s7KYiMMBrVhGSRJIW/mhcOhd4HNv+XB53YqrBfAmCny360mIdAErQvuc8ln
ojrSKWpq3E1JDpQ0E0BYQI3Lv1Gb0pyQG3QEsxBlMpzFJB5PKwSZu7BmgNSD5Bv2
ZY37vuSi7Wy0vkBDJfV75KBCVACcDY8BUZgEhXUZ92YRVa7Qx/FT7Fd73tzRsnF9
EqoiKFT97jVY3typkEa8VDS/dUt6VfztVR4VwuhtgQ8LfSSzKPuno7PqbyxicdE/
HBULGrSszjP1SrCKzGMpjuJj6HCTwPGmsTrsGWfQrltXG5/pZoT/41oojrP5zHCk
Cx1AU08ZP56VmOaMGSf1odYJ2Z1vrznk7nvM2SggVihnquMQekHE/YBgPsGXzPm4
MIwUaqv2y/oKlHnWaYh/Iwuxjys6yoyEBWLl5Yo/2YKOTLetqVAQ7tRg8PQP0+vB
5CUmPA0X4xD0iHnCRkinUk/SXhfBgjA8Dnh9Uh6aBotvX2PGheeVJAbhnPpkg40i
joQwV0kJq8blrg8jY9OgN/tPYo8GX9YTEpXK5ryizj9I5r9s/0wKxGPPIBRb4r3c
s8uAsv4a
=wCJU
-----END PGP SIGNATURE-----