summary refs log tree commit diff
path: root/nixos/doc/manual/from_md/development/settings-options.section.xml
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/doc/manual/from_md/development/settings-options.section.xml')
-rw-r--r--nixos/doc/manual/from_md/development/settings-options.section.xml389
1 files changed, 389 insertions, 0 deletions
diff --git a/nixos/doc/manual/from_md/development/settings-options.section.xml b/nixos/doc/manual/from_md/development/settings-options.section.xml
new file mode 100644
index 00000000000..746011a2d07
--- /dev/null
+++ b/nixos/doc/manual/from_md/development/settings-options.section.xml
@@ -0,0 +1,389 @@
+<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>
+      <varlistentry>
+        <term>
+          <literal>pkgs.formats.elixirConf { elixir ? pkgs.elixir }</literal>
+        </term>
+        <listitem>
+          <para>
+            A function taking an attribute set with values
+          </para>
+          <variablelist>
+            <varlistentry>
+              <term>
+                <literal>elixir</literal>
+              </term>
+              <listitem>
+                <para>
+                  The Elixir package which will be used to format the
+                  generated output
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+          <para>
+            It returns a set with Elixir-Config-specific attributes
+            <literal>type</literal>, <literal>lib</literal>, and
+            <literal>generate</literal> as specified
+            <link linkend="pkgs-formats-result">below</link>.
+          </para>
+          <para>
+            The <literal>lib</literal> attribute contains functions to
+            be used in settings, for generating special Elixir values:
+          </para>
+          <variablelist>
+            <varlistentry>
+              <term>
+                <literal>mkRaw elixirCode</literal>
+              </term>
+              <listitem>
+                <para>
+                  Outputs the given string as raw Elixir code
+                </para>
+              </listitem>
+            </varlistentry>
+            <varlistentry>
+              <term>
+                <literal>mkGetEnv { envVariable, fallback ? null }</literal>
+              </term>
+              <listitem>
+                <para>
+                  Makes the configuration fetch an environment variable
+                  at runtime
+                </para>
+              </listitem>
+            </varlistentry>
+            <varlistentry>
+              <term>
+                <literal>mkAtom atom</literal>
+              </term>
+              <listitem>
+                <para>
+                  Outputs the given string as an Elixir atom, instead of
+                  the default Elixir binary string. Note: lowercase
+                  atoms still needs to be prefixed with
+                  <literal>:</literal>
+                </para>
+              </listitem>
+            </varlistentry>
+            <varlistentry>
+              <term>
+                <literal>mkTuple array</literal>
+              </term>
+              <listitem>
+                <para>
+                  Outputs the given array as an Elixir tuple, instead of
+                  the default Elixir list
+                </para>
+              </listitem>
+            </varlistentry>
+            <varlistentry>
+              <term>
+                <literal>mkMap attrset</literal>
+              </term>
+              <listitem>
+                <para>
+                  Outputs the given attribute set as an Elixir map,
+                  instead of the default Elixir keyword list
+                </para>
+              </listitem>
+            </varlistentry>
+          </variablelist>
+        </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>lib</literal>
+        </term>
+        <listitem>
+          <para>
+            Utility functions for convenience, or special interactions
+            with the format. This attribute is optional. It may contain
+            inside a <literal>types</literal> attribute containing types
+            specific to this 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>