nixpkgs/nixos/doc/manual/from_md/development/settings-options.section.xml

286 lines
10 KiB
XML

<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xml:id="sec-settings-options">
<title>Options for Program Settings</title>
<para>
Many programs have configuration files where program-specific
settings can be declared. File formats can be separated into two
categories:
</para>
<itemizedlist>
<listitem>
<para>
Nix-representable ones: These can trivially be mapped to a
subset of Nix syntax. E.g. JSON is an example, since its values
like <literal>{&quot;foo&quot;:{&quot;bar&quot;:10}}</literal>
can be mapped directly to Nix:
<literal>{ foo = { bar = 10; }; }</literal>. Other examples are
INI, YAML and TOML. The following section explains the
convention for these settings.
</para>
</listitem>
<listitem>
<para>
Non-nix-representable ones: These can't be trivially mapped to a
subset of Nix syntax. Most generic programming languages are in
this group, e.g. bash, since the statement
<literal>if true; then echo hi; fi</literal> doesn't have a
trivial representation in Nix.
</para>
<para>
Currently there are no fixed conventions for these, but it is
common to have a <literal>configFile</literal> option for
setting the configuration file path directly. The default value
of <literal>configFile</literal> can be an auto-generated file,
with convenient options for controlling the contents. For
example an option of type <literal>attrsOf str</literal> can be
used for representing environment variables which generates a
section like <literal>export FOO=&quot;foo&quot;</literal>.
Often it can also be useful to also include an
<literal>extraConfig</literal> option of type
<literal>lines</literal> to allow arbitrary text after the
autogenerated part of the file.
</para>
</listitem>
</itemizedlist>
<section xml:id="sec-settings-nix-representable">
<title>Nix-representable Formats (JSON, YAML, TOML, INI,
...)</title>
<para>
By convention, formats like this are handled with a generic
<literal>settings</literal> option, representing the full program
configuration as a Nix value. The type of this option should
represent the format. The most common formats have a predefined
type and string generator already declared under
<literal>pkgs.formats</literal>:
</para>
<variablelist>
<varlistentry>
<term>
<literal>pkgs.formats.json</literal> { }
</term>
<listitem>
<para>
A function taking an empty attribute set (for future
extensibility) and returning a set with JSON-specific
attributes <literal>type</literal> and
<literal>generate</literal> as specified
<link linkend="pkgs-formats-result">below</link>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>pkgs.formats.yaml</literal> { }
</term>
<listitem>
<para>
A function taking an empty attribute set (for future
extensibility) and returning a set with YAML-specific
attributes <literal>type</literal> and
<literal>generate</literal> as specified
<link linkend="pkgs-formats-result">below</link>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>pkgs.formats.ini</literal> {
<emphasis><literal>listsAsDuplicateKeys</literal></emphasis> ?
false, <emphasis><literal>listToValue</literal></emphasis> ?
null, ... }
</term>
<listitem>
<para>
A function taking an attribute set with values
</para>
<variablelist>
<varlistentry>
<term>
<literal>listsAsDuplicateKeys</literal>
</term>
<listitem>
<para>
A boolean for controlling whether list values can be
used to represent duplicate INI keys
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>listToValue</literal>
</term>
<listitem>
<para>
A function for turning a list of values into a single
value.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
It returns a set with INI-specific attributes
<literal>type</literal> and <literal>generate</literal> as
specified <link linkend="pkgs-formats-result">below</link>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>pkgs.formats.toml</literal> { }
</term>
<listitem>
<para>
A function taking an empty attribute set (for future
extensibility) and returning a set with TOML-specific
attributes <literal>type</literal> and
<literal>generate</literal> as specified
<link linkend="pkgs-formats-result">below</link>.
</para>
</listitem>
</varlistentry>
</variablelist>
<para xml:id="pkgs-formats-result">
These functions all return an attribute set with these values:
</para>
<variablelist>
<varlistentry>
<term>
<literal>type</literal>
</term>
<listitem>
<para>
A module system type representing a value of the format
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>generate</literal>
<emphasis><literal>filename jsonValue</literal></emphasis>
</term>
<listitem>
<para>
A function that can render a value of the format to a file.
Returns a file path.
</para>
<note>
<para>
This function puts the value contents in the Nix store. So
this should be avoided for secrets.
</para>
</note>
</listitem>
</varlistentry>
</variablelist>
<anchor xml:id="ex-settings-nix-representable" />
<para>
<emphasis role="strong">Example: Module with conventional
<literal>settings</literal> option</emphasis>
</para>
<para>
The following shows a module for an example program that uses a
JSON configuration file. It demonstrates how above values can be
used, along with some other related best practices. See the
comments for explanations.
</para>
<programlisting language="bash">
{ options, config, lib, pkgs, ... }:
let
cfg = config.services.foo;
# Define the settings format used for this program
settingsFormat = pkgs.formats.json {};
in {
options.services.foo = {
enable = lib.mkEnableOption &quot;foo service&quot;;
settings = lib.mkOption {
# Setting this type allows for correct merging behavior
type = settingsFormat.type;
default = {};
description = ''
Configuration for foo, see
&lt;link xlink:href=&quot;https://example.com/docs/foo&quot;/&gt;
for supported settings.
'';
};
};
config = lib.mkIf cfg.enable {
# We can assign some default settings here to make the service work by just
# enabling it. We use `mkDefault` for values that can be changed without
# problems
services.foo.settings = {
# Fails at runtime without any value set
log_level = lib.mkDefault &quot;WARN&quot;;
# We assume systemd's `StateDirectory` is used, so we require this value,
# therefore no mkDefault
data_path = &quot;/var/lib/foo&quot;;
# Since we use this to create a user we need to know the default value at
# eval time
user = lib.mkDefault &quot;foo&quot;;
};
environment.etc.&quot;foo.json&quot;.source =
# The formats generator function takes a filename and the Nix value
# representing the format value and produces a filepath with that value
# rendered in the format
settingsFormat.generate &quot;foo-config.json&quot; cfg.settings;
# We know that the `user` attribute exists because we set a default value
# for it above, allowing us to use it without worries here
users.users.${cfg.settings.user} = { isSystemUser = true; };
# ...
};
}
</programlisting>
<section xml:id="sec-settings-attrs-options">
<title>Option declarations for attributes</title>
<para>
Some <literal>settings</literal> attributes may deserve some
extra care. They may need a different type, default or merging
behavior, or they are essential options that should show their
documentation in the manual. This can be done using
<xref linkend="sec-freeform-modules" />.
</para>
<para>
We extend above example using freeform modules to declare an
option for the port, which will enforce it to be a valid integer
and make it show up in the manual.
</para>
<anchor xml:id="ex-settings-typed-attrs" />
<para>
<emphasis role="strong">Example: Declaring a type-checked
<literal>settings</literal> attribute</emphasis>
</para>
<programlisting language="bash">
settings = lib.mkOption {
type = lib.types.submodule {
freeformType = settingsFormat.type;
# Declare an option for the port such that the type is checked and this option
# is shown in the manual.
options.port = lib.mkOption {
type = lib.types.port;
default = 8080;
description = ''
Which port this service should listen on.
'';
};
};
default = {};
description = ''
Configuration for Foo, see
&lt;link xlink:href=&quot;https://example.com/docs/foo&quot;/&gt;
for supported values.
'';
};
</programlisting>
</section>
</section>
</section>