summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--lib/modules.nix1
-rw-r--r--lib/types.nix117
-rw-r--r--nixos/doc/manual/development/option-types.xml11
-rw-r--r--nixos/doc/manual/release-notes/rl-2009.xml12
-rw-r--r--nixos/modules/config/users-groups.nix4
-rw-r--r--nixos/modules/installer/cd-dvd/sd-image-raspberrypi4.nix2
-rw-r--r--nixos/modules/installer/tools/nixos-option/nixos-option.cc2
-rw-r--r--nixos/modules/programs/ssh.nix2
-rw-r--r--nixos/modules/programs/tsm-client.nix4
-rw-r--r--nixos/modules/security/pam.nix2
-rw-r--r--nixos/modules/services/backup/znapzend.nix4
-rw-r--r--nixos/modules/services/desktops/geoclue2.nix2
-rw-r--r--nixos/modules/services/hardware/sane_extra_backends/brscan4.nix2
-rw-r--r--nixos/modules/services/networking/hylafax/options.nix4
-rw-r--r--nixos/modules/services/networking/nylon.nix2
-rw-r--r--nixos/modules/services/networking/prosody.nix2
-rw-r--r--nixos/modules/services/networking/ssh/sshd.nix2
-rw-r--r--nixos/modules/services/security/tor.nix2
-rw-r--r--nixos/modules/system/boot/luksroot.nix2
-rw-r--r--nixos/modules/system/boot/stage-1.nix2
-rw-r--r--nixos/modules/system/etc/etc.nix2
-rw-r--r--nixos/modules/tasks/encrypted-devices.nix2
-rw-r--r--nixos/modules/tasks/filesystems.nix4
-rw-r--r--nixos/modules/tasks/network-interfaces.nix4
-rw-r--r--nixos/modules/virtualisation/nixos-containers.nix2
-rw-r--r--nixos/modules/virtualisation/railcar.nix4
26 files changed, 52 insertions, 147 deletions
diff --git a/lib/modules.nix b/lib/modules.nix
index 2ec34699809..decb96ffe11 100644
--- a/lib/modules.nix
+++ b/lib/modules.nix
@@ -613,7 +613,6 @@ rec {
         if tp.name == "option set" || tp.name == "submodule" then
           throw "The option ${showOption loc} uses submodules without a wrapping type, in ${showFiles opt.declarations}."
         else if optionSetIn "attrsOf" then types.attrsOf (types.submodule options)
-        else if optionSetIn "loaOf"   then types.loaOf   (types.submodule options)
         else if optionSetIn "listOf"  then types.listOf  (types.submodule options)
         else if optionSetIn "nullOr"  then types.nullOr  (types.submodule options)
         else tp;
diff --git a/lib/types.nix b/lib/types.nix
index 1845b6ae339..17e7a939fe3 100644
--- a/lib/types.nix
+++ b/lib/types.nix
@@ -252,8 +252,8 @@ rec {
       merge = mergeEqualOption;
     };
 
-    # drop this in the future:
-    list = builtins.trace "`types.list` is deprecated; use `types.listOf` instead" types.listOf;
+    # TODO: drop this in the future:
+    list = builtins.trace "`types.list` has been removed; please use `types.listOf` instead" types.listOf;
 
     listOf = elemType: mkOptionType rec {
       name = "listOf";
@@ -326,110 +326,15 @@ rec {
       functor = (defaultFunctor name) // { wrapped = elemType; };
     };
 
-    # List or attribute set of ...
-    loaOf = elemType:
-      let
-        convertAllLists = loc: defs:
-          let
-            padWidth = stringLength (toString (length defs));
-            unnamedPrefix = i: "unnamed-" + fixedWidthNumber padWidth i + ".";
-          in
-            imap1 (i: convertIfList loc (unnamedPrefix i)) defs;
-        convertIfList = loc: unnamedPrefix: def:
-          if isList def.value then
-            let
-              padWidth = stringLength (toString (length def.value));
-              unnamed = i: unnamedPrefix + fixedWidthNumber padWidth i;
-              anyString = placeholder "name";
-              nameAttrs = [
-                { path = [ "environment" "etc" ];
-                  name = "target";
-                }
-                { path = [ "containers" anyString "bindMounts" ];
-                  name = "mountPoint";
-                }
-                { path = [ "programs" "ssh" "knownHosts" ];
-                  # hostNames is actually a list so we would need to handle it only when singleton
-                  name = "hostNames";
-                }
-                { path = [ "fileSystems" ];
-                  name = "mountPoint";
-                }
-                { path = [ "boot" "specialFileSystems" ];
-                  name = "mountPoint";
-                }
-                { path = [ "services" "znapzend" "zetup" ];
-                  name = "dataset";
-                }
-                { path = [ "services" "znapzend" "zetup" anyString "destinations" ];
-                  name = "label";
-                }
-                { path = [ "services" "geoclue2" "appConfig" ];
-                  name = "desktopID";
-                }
-              ];
-              matched = let
-                equals = a: b: b == anyString || a == b;
-                fallback = { name = "name"; };
-              in findFirst ({ path, ... }: all (v: v == true) (zipListsWith equals loc path)) fallback nameAttrs;
-              nameAttr = matched.name;
-              nameValueOld = value:
-                if isList value then
-                  if length value > 0 then
-                    "[ " + concatMapStringsSep " " escapeNixString value + " ]"
-                  else
-                    "[ ]"
-                else
-                  escapeNixString value;
-              nameValueNew = value: unnamed:
-                if isList value then
-                  if length value > 0 then
-                    head value
-                  else
-                    unnamed
-                else
-                  value;
-              res =
-                { inherit (def) file;
-                  value = listToAttrs (
-                    imap1 (elemIdx: elem:
-                      { name  = nameValueNew (elem.${nameAttr} or (unnamed elemIdx)) (unnamed elemIdx);
-                        value = elem;
-                      }) def.value);
-                };
-              option = concatStringsSep "." loc;
-              sample = take 3 def.value;
-              more = lib.optionalString (length def.value > 3) "... ";
-              list = concatMapStrings (x: ''{ ${nameAttr} = ${nameValueOld (x.${nameAttr} or "unnamed")}; ...} '') sample;
-              set = concatMapStrings (x: ''${nameValueNew (x.${nameAttr} or "unnamed") "unnamed"} = {...}; '') sample;
-              msg = ''
-                In file ${def.file}
-                a list is being assigned to the option config.${option}.
-                This will soon be an error as type loaOf is deprecated.
-                See https://github.com/NixOS/nixpkgs/pull/63103 for more information.
-                Do
-                  ${option} =
-                    { ${set}${more}}
-                instead of
-                  ${option} =
-                    [ ${list}${more}]
-              '';
-            in
-              lib.warn msg res
-          else
-            def;
-        attrOnly = attrsOf elemType;
-      in mkOptionType rec {
-        name = "loaOf";
-        description = "list or attribute set of ${elemType.description}s";
-        check = x: isList x || isAttrs x;
-        merge = loc: defs: attrOnly.merge loc (convertAllLists loc defs);
-        emptyValue = { value = {}; };
-        getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name?>"]);
-        getSubModules = elemType.getSubModules;
-        substSubModules = m: loaOf (elemType.substSubModules m);
-        functor = (defaultFunctor name) // { wrapped = elemType; };
-      };
+    # TODO: drop this in the future:
+    loaOf =
+      let msg =
+        ''
+          `types.loaOf` has been removed and mixing lists with attribute values
+          is no longer possible; please use `types.attrsOf` instead.
+          See https://github.com/NixOS/nixpkgs/issues/1800 for the motivation.
+        '';
+      in builtins.trace msg types.attrsOf;
 
     # Value of given type but with no merging (i.e. `uniq list`s are not concatenated).
     uniq = elemType: mkOptionType rec {
diff --git a/nixos/doc/manual/development/option-types.xml b/nixos/doc/manual/development/option-types.xml
index 957349ad181..5a6dae6e991 100644
--- a/nixos/doc/manual/development/option-types.xml
+++ b/nixos/doc/manual/development/option-types.xml
@@ -387,17 +387,6 @@
    </varlistentry>
    <varlistentry>
     <term>
-     <varname>types.loaOf</varname> <replaceable>t</replaceable>
-    </term>
-    <listitem>
-     <para>
-      An attribute set or a list of <replaceable>t</replaceable> type. Multiple
-      definitions are merged according to the value.
-     </para>
-    </listitem>
-   </varlistentry>
-   <varlistentry>
-    <term>
      <varname>types.nullOr</varname> <replaceable>t</replaceable>
     </term>
     <listitem>
diff --git a/nixos/doc/manual/release-notes/rl-2009.xml b/nixos/doc/manual/release-notes/rl-2009.xml
index 83bc2f82bbf..bf96fb21a41 100644
--- a/nixos/doc/manual/release-notes/rl-2009.xml
+++ b/nixos/doc/manual/release-notes/rl-2009.xml
@@ -743,6 +743,18 @@ CREATE ROLE postgres LOGIN SUPERUSER;
      See <link xlink:href="https://github.com/NixOS/nixpkgs/pull/82743#issuecomment-674520472">the PR that changed this</link> for more info.
     </para>
    </listitem>
+   <listitem>
+    <para>
+     For NixOS configuration options, the type <literal>loaOf</literal>, after
+     its initial deprecation in release 20.03, has been removed. In NixOS and
+     Nixpkgs options using this type have been converted to <literal>attrsOf</literal>.
+    </para>
+    <para>
+     For more information on this change have look at these links:
+     <link xlink:href="https://github.com/NixOS/nixpkgs/issues/1800">issue #1800</link>,
+     <link xlink:href="https://github.com/NixOS/nixpkgs/pull/63103">PR #63103</link>.
+    </para>
+   </listitem>
   </itemizedlist>
  </section>
 
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index 56b7af98b61..0ab303d0ae4 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -463,7 +463,7 @@ in {
 
     users.users = mkOption {
       default = {};
-      type = with types; loaOf (submodule userOpts);
+      type = with types; attrsOf (submodule userOpts);
       example = {
         alice = {
           uid = 1234;
@@ -487,7 +487,7 @@ in {
         { students.gid = 1001;
           hackers = { };
         };
-      type = with types; loaOf (submodule groupOpts);
+      type = with types; attrsOf (submodule groupOpts);
       description = ''
         Additional groups to be created automatically by the system.
       '';
diff --git a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi4.nix b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi4.nix
index 79c835dc390..87545e84203 100644
--- a/nixos/modules/installer/cd-dvd/sd-image-raspberrypi4.nix
+++ b/nixos/modules/installer/cd-dvd/sd-image-raspberrypi4.nix
@@ -27,7 +27,7 @@
   };
 
   fileSystems."/boot/firmware" = {
-    # This effectively "renames" the loaOf entry set in sd-image.nix
+    # This effectively "renames" the attrsOf entry set in sd-image.nix
     mountPoint = "/boot";
     neededForBoot = true;
   };
diff --git a/nixos/modules/installer/tools/nixos-option/nixos-option.cc b/nixos/modules/installer/tools/nixos-option/nixos-option.cc
index 1a7b07a74f8..f779d82edbd 100644
--- a/nixos/modules/installer/tools/nixos-option/nixos-option.cc
+++ b/nixos/modules/installer/tools/nixos-option/nixos-option.cc
@@ -224,7 +224,7 @@ bool optionTypeIs(Context & ctx, Value & v, const std::string & soughtType)
 
 bool isAggregateOptionType(Context & ctx, Value & v)
 {
-    return optionTypeIs(ctx, v, "attrsOf") || optionTypeIs(ctx, v, "listOf") || optionTypeIs(ctx, v, "loaOf");
+    return optionTypeIs(ctx, v, "attrsOf") || optionTypeIs(ctx, v, "listOf");
 }
 
 MakeError(OptionPathError, EvalError);
diff --git a/nixos/modules/programs/ssh.nix b/nixos/modules/programs/ssh.nix
index a983ffa4b89..40af4d0ff5a 100644
--- a/nixos/modules/programs/ssh.nix
+++ b/nixos/modules/programs/ssh.nix
@@ -131,7 +131,7 @@ in
 
       knownHosts = mkOption {
         default = {};
-        type = types.loaOf (types.submodule ({ name, ... }: {
+        type = types.attrsOf (types.submodule ({ name, ... }: {
           options = {
             certAuthority = mkOption {
               type = types.bool;
diff --git a/nixos/modules/programs/tsm-client.nix b/nixos/modules/programs/tsm-client.nix
index eb6f1247528..7ac4086d5f0 100644
--- a/nixos/modules/programs/tsm-client.nix
+++ b/nixos/modules/programs/tsm-client.nix
@@ -7,7 +7,7 @@ let
   inherit (lib.modules) mkDefault mkIf;
   inherit (lib.options) literalExample mkEnableOption mkOption;
   inherit (lib.strings) concatStringsSep optionalString toLower;
-  inherit (lib.types) addCheck attrsOf lines loaOf nullOr package path port str strMatching submodule;
+  inherit (lib.types) addCheck attrsOf lines nullOr package path port str strMatching submodule;
 
   # Checks if given list of strings contains unique
   # elements when compared without considering case.
@@ -178,7 +178,7 @@ let
       client system-options file "dsm.sys"
     '';
     servers = mkOption {
-      type = loaOf (submodule [ serverOptions ]);
+      type = attrsOf (submodule [ serverOptions ]);
       default = {};
       example.mainTsmServer = {
         server = "tsmserver.company.com";
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 565c15dec24..ce74805ef41 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -544,7 +544,7 @@ in
 
     security.pam.services = mkOption {
       default = [];
-      type = with types; loaOf (submodule pamOpts);
+      type = with types; attrsOf (submodule pamOpts);
       description =
         ''
           This option defines the PAM services.  A service typically
diff --git a/nixos/modules/services/backup/znapzend.nix b/nixos/modules/services/backup/znapzend.nix
index 8098617d11f..0ca71b413ce 100644
--- a/nixos/modules/services/backup/znapzend.nix
+++ b/nixos/modules/services/backup/znapzend.nix
@@ -220,7 +220,7 @@ let
       };
 
       destinations = mkOption {
-        type = loaOf (destType config);
+        type = attrsOf (destType config);
         description = "Additional destinations.";
         default = {};
         example = literalExample ''
@@ -328,7 +328,7 @@ in
       };
 
       zetup = mkOption {
-        type = loaOf srcType;
+        type = attrsOf srcType;
         description = "Znapzend configuration.";
         default = {};
         example = literalExample ''
diff --git a/nixos/modules/services/desktops/geoclue2.nix b/nixos/modules/services/desktops/geoclue2.nix
index 542b2ead410..6702bd395a0 100644
--- a/nixos/modules/services/desktops/geoclue2.nix
+++ b/nixos/modules/services/desktops/geoclue2.nix
@@ -160,7 +160,7 @@ in
       };
 
       appConfig = mkOption {
-        type = types.loaOf appConfigModule;
+        type = types.attrsOf appConfigModule;
         default = {};
         example = literalExample ''
           "com.github.app" = {
diff --git a/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix b/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix
index 6f49a1ab6d4..a6afa01dd81 100644
--- a/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix
+++ b/nixos/modules/services/hardware/sane_extra_backends/brscan4.nix
@@ -81,7 +81,7 @@ in
         { office1 = { model = "MFC-7860DW"; ip = "192.168.1.2"; };
           office2 = { model = "MFC-7860DW"; nodename = "BRW0080927AFBCE"; };
         };
-      type = with types; loaOf (submodule netDeviceOpts);
+      type = with types; attrsOf (submodule netDeviceOpts);
       description = ''
         The list of network devices that will be registered against the brscan4
         sane backend.
diff --git a/nixos/modules/services/networking/hylafax/options.nix b/nixos/modules/services/networking/hylafax/options.nix
index 4ac6d3fa843..9e28d09dffc 100644
--- a/nixos/modules/services/networking/hylafax/options.nix
+++ b/nixos/modules/services/networking/hylafax/options.nix
@@ -3,7 +3,7 @@
 let
 
   inherit (lib.options) literalExample mkEnableOption mkOption;
-  inherit (lib.types) bool enum int lines loaOf nullOr path str submodule;
+  inherit (lib.types) bool enum int lines attrsOf nullOr path str submodule;
   inherit (lib.modules) mkDefault mkIf mkMerge;
 
   commonDescr = ''
@@ -248,7 +248,7 @@ in
     };
 
     modems = mkOption {
-      type = loaOf (submodule [ modemConfigOptions ]);
+      type = attrsOf (submodule [ modemConfigOptions ]);
       default = {};
       example.ttyS1 = {
         type = "cirrus";
diff --git a/nixos/modules/services/networking/nylon.nix b/nixos/modules/services/networking/nylon.nix
index 7c171281a92..bfc358cb12f 100644
--- a/nixos/modules/services/networking/nylon.nix
+++ b/nixos/modules/services/networking/nylon.nix
@@ -140,7 +140,7 @@ in
     services.nylon = mkOption {
       default = {};
       description = "Collection of named nylon instances";
-      type = with types; loaOf (submodule nylonOpts);
+      type = with types; attrsOf (submodule nylonOpts);
       internal = true;
     };
 
diff --git a/nixos/modules/services/networking/prosody.nix b/nixos/modules/services/networking/prosody.nix
index e53d7093be8..a6c1cb0f479 100644
--- a/nixos/modules/services/networking/prosody.nix
+++ b/nixos/modules/services/networking/prosody.nix
@@ -655,7 +655,7 @@ in
 
         description = "Define the virtual hosts";
 
-        type = with types; loaOf (submodule vHostOpts);
+        type = with types; attrsOf (submodule vHostOpts);
 
         example = {
           myhost = {
diff --git a/nixos/modules/services/networking/ssh/sshd.nix b/nixos/modules/services/networking/ssh/sshd.nix
index 17f31e3a488..6b27cbc56bd 100644
--- a/nixos/modules/services/networking/ssh/sshd.nix
+++ b/nixos/modules/services/networking/ssh/sshd.nix
@@ -361,7 +361,7 @@ in
     };
 
     users.users = mkOption {
-      type = with types; loaOf (submodule userOptions);
+      type = with types; attrsOf (submodule userOptions);
     };
 
   };
diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix
index b33e905c67d..885b23ac694 100644
--- a/nixos/modules/services/security/tor.nix
+++ b/nixos/modules/services/security/tor.nix
@@ -597,7 +597,7 @@ in
             ];
           }
         '';
-        type = types.loaOf (types.submodule ({name, ...}: {
+        type = types.attrsOf (types.submodule ({name, ...}: {
           options = {
 
              name = mkOption {
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index 166f89c7066..a4cbb03a8e3 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -516,7 +516,7 @@ in
         <filename>/dev/mapper/<replaceable>name</replaceable></filename>.
       '';
 
-      type = with types; loaOf (submodule (
+      type = with types; attrsOf (submodule (
         { name, ... }: { options = {
 
           name = mkOption {
diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix
index a04660fb56e..97eb1d286a3 100644
--- a/nixos/modules/system/boot/stage-1.nix
+++ b/nixos/modules/system/boot/stage-1.nix
@@ -555,7 +555,7 @@ in
       };
 
     fileSystems = mkOption {
-      type = with lib.types; loaOf (submodule {
+      type = with lib.types; attrsOf (submodule {
         options.neededForBoot = mkOption {
           default = false;
           type = types.bool;
diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix
index 1f4d54a1ae2..7478e3e8071 100644
--- a/nixos/modules/system/etc/etc.nix
+++ b/nixos/modules/system/etc/etc.nix
@@ -46,7 +46,7 @@ in
         Set of files that have to be linked in <filename>/etc</filename>.
       '';
 
-      type = with types; loaOf (submodule (
+      type = with types; attrsOf (submodule (
         { name, config, ... }:
         { options = {
 
diff --git a/nixos/modules/tasks/encrypted-devices.nix b/nixos/modules/tasks/encrypted-devices.nix
index 9c3f2d8fccb..dd337de9869 100644
--- a/nixos/modules/tasks/encrypted-devices.nix
+++ b/nixos/modules/tasks/encrypted-devices.nix
@@ -54,7 +54,7 @@ in
 
   options = {
     fileSystems = mkOption {
-      type = with lib.types; loaOf (submodule encryptedFSOptions);
+      type = with lib.types; attrsOf (submodule encryptedFSOptions);
     };
     swapDevices = mkOption {
       type = with lib.types; listOf (submodule encryptedFSOptions);
diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix
index 0ade74b957a..3ea67dac714 100644
--- a/nixos/modules/tasks/filesystems.nix
+++ b/nixos/modules/tasks/filesystems.nix
@@ -159,7 +159,7 @@ in
           "/bigdisk".label = "bigdisk";
         }
       '';
-      type = types.loaOf (types.submodule [coreFileSystemOpts fileSystemOpts]);
+      type = types.attrsOf (types.submodule [coreFileSystemOpts fileSystemOpts]);
       description = ''
         The file systems to be mounted.  It must include an entry for
         the root directory (<literal>mountPoint = "/"</literal>).  Each
@@ -193,7 +193,7 @@ in
 
     boot.specialFileSystems = mkOption {
       default = {};
-      type = types.loaOf (types.submodule coreFileSystemOpts);
+      type = types.attrsOf (types.submodule coreFileSystemOpts);
       internal = true;
       description = ''
         Special filesystems that are mounted very early during boot.
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 565868724ad..2e533fb604e 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -519,7 +519,7 @@ in
         <option>networking.useDHCP</option> is true, then every
         interface not listed here will be configured using DHCP.
       '';
-      type = with types; loaOf (submodule interfaceOpts);
+      type = with types; attrsOf (submodule interfaceOpts);
     };
 
     networking.vswitches = mkOption {
@@ -544,7 +544,7 @@ in
           interfaces = mkOption {
             example = [ "eth0" "eth1" ];
             description = "The physical network interfaces connected by the vSwitch.";
-            type = with types; loaOf (submodule vswitchInterfaceOpts);
+            type = with types; attrsOf (submodule vswitchInterfaceOpts);
           };
 
           controllers = mkOption {
diff --git a/nixos/modules/virtualisation/nixos-containers.nix b/nixos/modules/virtualisation/nixos-containers.nix
index b0fa03917c8..8fbb4efd201 100644
--- a/nixos/modules/virtualisation/nixos-containers.nix
+++ b/nixos/modules/virtualisation/nixos-containers.nix
@@ -627,7 +627,7 @@ in
 		    };
 
             bindMounts = mkOption {
-              type = with types; loaOf (submodule bindMountOpts);
+              type = with types; attrsOf (submodule bindMountOpts);
               default = {};
               example = literalExample ''
                 { "/home" = { hostPath = "/home/alice";
diff --git a/nixos/modules/virtualisation/railcar.nix b/nixos/modules/virtualisation/railcar.nix
index 3f188fc68e5..10464f62898 100644
--- a/nixos/modules/virtualisation/railcar.nix
+++ b/nixos/modules/virtualisation/railcar.nix
@@ -41,7 +41,7 @@ let
         description = "Source for the in-container mount";
       };
       options = mkOption {
-        type = loaOf (str);
+        type = attrsOf (str);
         default = [ "bind" ];
         description = ''
           Mount options of the filesystem to be used.
@@ -61,7 +61,7 @@ in
     containers = mkOption {
       default = {};
       description = "Declarative container configuration";
-      type = with types; loaOf (submodule ({ name, config, ... }: {
+      type = with types; attrsOf (submodule ({ name, config, ... }: {
         options = {
           cmd = mkOption {
             type = types.lines;