Need help for expanding a genAttrs based systemconfig approach

Hello! At the moment I have a flake that generates my system configurations for a small set of identical systems based on genAttrs. It looks like this:

inputs: 
  let
    system = "x86_64-linux";
    hosts = [ "node01" "node02" ];
    pkgs = nixpkgs.legacyPackages.${system};
  in 
  {
      nixosConfigurations = pkgs.lib.genAttrs hosts (host: 
        import ./systems/node { inherit inputs system pkgs host; } );
  }

This works fine, but I managed to modularize my config even more and basically just need to set the Hostname and two IP Addresses for each system.
So I thought I would create either a list of attributes or an attribute set with all the parameters for each system.

So changing the hosts list to something like

hosts =   
   [ 
      { name = "node01"; ip = "192.168.88.31/24"; initIp = "192.168.88.21/24"; } 
      { name = "node02"; ip = "192.168.88.32/24"; initIp = "192.168.88.22/24"; } 
   ];

or

hosts = 
{ 
   node01 = [ "node01" "192.168.88.31/24" "192.168.88.21/24" ];
   node02 = [ "node02" "192.168.88.32/24" "192.168.88.22/24" ];
 } 

But I’m unable to alter the current genAttrs part of my flake make it work. I tried listToAttrs and foreach but I’m not able to get it working. For starters I’m not even sure if the list of attribute sets would be easier than a solution with the attribute set containig lists.
This is probably really basic to most but I haven’t touched nix for a while so I’m completely stuck atm.

Any help or hint would be appreciated.

It seems like you want something like this as a starting point:

builtins.listToAttrs (map ({ name, ip, initIp }: <your code here>) hosts);

And it might be more readable to pull the mapping function out into a let-binding:

let
  system = "x86_64-linux";
  hosts = [ 
    { name = "node01"; ip = "192.168.88.31/24"; initIp = "192.168.88.21/24"; } 
    { name = "node02"; ip = "192.168.88.32/24"; initIp = "192.168.88.22/24"; } 
  ];
  cfgForHost =
    { name, ip, initIp }:
    <your code here>;
  pkgs = nixpkgs.legacyPackages.${system};
in 
{
  nixosConfigurations = builtins.listToAttrs (map cfgForHost hosts);
}

It would. :slight_smile:

Thank you! This helps a lot. At least I now know in which direction I need to go.
I can’t get it to work with my import statement atm. but I guess I will get there with some more digging.
Thank you again!

Ok, I guess it isn’t that easy to dig into.

I tried the following:

 let
    # Global configuration for my systems
    globals.user = "manji";
    system = "x86_64-linux";
    basePath = ./.;
    pkgs = nixpkgs.legacyPackages.${system};
    hosts = 
    [ 
      { name = "node01"; ip = "192.168.88.31/24"; initIp = "192.168.88.21/24"; } 
      { name = "node02"; ip = "192.168.88.32/24"; initIp = "192.168.88.22/24"; } 
    ];
    cfgForHost = { name, ip, initIp }: (import ./systems/node) { inherit inputs globals basePath system pkgs name ip initIp; };
  in 
  {
    nixosConfigurations =  builtins.listToAttrs (builtins.map cfgForHost hosts);
  }

hosts is ok, cfgForHosts seems fine as well. Bit the mapping results in something completely odd. I dove into it using nix repl, but I couldn’t get very far.

Allplying the mapping seems to work fine:

nix-repl> builtins.map cfgForHost hosts                                                                             [
  { ... }
  { ... }
]

And a further peek at one of the resulting elements:

nix-repl> builtins.elemAt (builtins.map cfgForHost hosts) 0 
{
  _module = { ... };
  _type = "configuration";
  class = "nixos";
  config = { ... };
  extendModules = «lambda extendModules @ /nix/store/vlq3lnsh2xij0d9blab16pq0sr09z4nl-source/nixos/lib/eval-config.nix:141:23»;
  extraArgs = { ... };
  graph = [ ... ];
  lib = { ... };
  options = { ... };
  pkgs = { ... };
  type = { ... };
}

Seems ok from what I can tell. I applied the mapping, the values have been applied to the imported configuration, all seems well.
But the final call to listToAttrs fails.

nix-repl> builtins.listToAttrs (builtins.map cfgForHost hosts)                        
error:
       … while calling the 'listToAttrs' builtin
         at «string»:1:1:
            1| builtins.listToAttrs (builtins.map cfgForHost hosts)
             | ^

       … in a {name=...; value=...;} pair

       error: attribute 'name' missing

From what I can see, the list that is returned after applying the mapping, is not what listToAttrs expects.

Do I need to wrap the mapping result in another mapping to make it compatible with the name=x; value=y format that listToAttrs expects?

Is there a way to get more detaile dinformation out of nix repl in this case or with some other mechanism?

Ah, yes, the cfgForHost function needs to return something that has name and value attributes, where value is the configuration.

Something like this:

  cfgForHost =
    { name, ip, initIp }:
    {
      inherit name;
      value = import systems/node { inherit inputs globals basePath system pkgs name ip initIp; };
    };

Ah damn, thank you. It would have taken ages for me to see this, so it was my import but it only (rightfully) complains during the call to listToAttrs. Now that I see the solution the side by side with the definition of listToAttrs it makes sense. Guess I got lost in unnecessary details. Thank you again for helping me with this!
You were also right about using a list of attribute sets. It makes things a lot easier. I can now extend these similar configs by a config that is totally different just by creating the list that contains the attribute set and concatenate this list with the rest of the configs right before calling listToAttrs.

    cfgForHost = 
      { name, ip, initIp }: 
      {
        inherit name;
        value = import ./systems/node { inherit inputs globals basePath system pkgs name ip initIp; };
      };
    usbInst = pkgs.lib.singleton 
      {
        name = "usbInst";
        value = import ./iso/usbinst { inherit inputs globals basePath system pkgs; };
      };
  in 
  {
    # Available through 'nixos-rebuild --flake .#your-hostname'
    nixosConfigurations =  builtins.listToAttrs (( builtins.map cfgForHost hosts ) ++ usbInst );
  }

Thanks again!