git.delta.rocks / jrsonnet / refs/commits / d9fb30d36ead

difftreelog

feat lenient nixosModules type

Yaroslav Bolyukin2024-07-11parent: #e9ac172.patch.diff
in: trunk

8 files changed

modifiedREADME.adocdiffbeforeafterboth
--- a/README.adoc
+++ b/README.adoc
@@ -63,18 +63,14 @@
       # nixosModules section of fleet config declares modules, which are used for all configured nixos hosts.
       nixosModules = [
         lanzaboote.nixosModules.lanzaboote
-        ({
-          config,
-          lib,
-          ...
-        }: {
+        {
           # Make `nix shell nixpkgs#thing` use the same nixpkgs, as used to build the system.
           nix.registry.nixpkgs = {
             from = { id = "nixpkgs"; type = "indirect"; };
             flake = nixpkgs;
             exact = false;
           };
-        })
+        }
       ];
 
       # Those modules are used to configure all the machines in cluster at the same time, good example of global modules
@@ -97,12 +93,12 @@
           ./controlplane-1/hardware-configuration.nix
           ./controlplane-1/configuration.nix
           # Configuration may also be specified inline, as in any nixos config.
-          ({...}: {
+          {
             services.ray = {
               gpus = 4;
               cpus = 128;
             };
-          })
+          }
         ];
       };
     };
modifiedflake.nixdiffbeforeafterboth
--- a/flake.nix
+++ b/flake.nix
@@ -16,19 +16,18 @@
       inputs.nixpkgs.follows = "nixpkgs";
     };
   };
-  outputs = {
+  outputs = inputs @ {
     self,
-    rust-overlay,
     flake-parts,
-    nixpkgs,
-    nixpkgs-stable-for-tests,
     crane,
+    ...
   }:
     flake-parts.lib.mkFlake {
-      # Not passing inputs through inputs for better visibility.
-      inputs = {};
+      inherit inputs;
     } {
-      flake = {
+      flake = let
+        inherit (inputs.nixpkgs.lib) mapAttrs;
+      in {
         lib = import ./lib {
           fleetPkgsForPkgs = pkgs:
             import ./pkgs {
@@ -45,11 +44,11 @@
             '';
             inventory = output: {
               children =
-                builtins.mapAttrs (configName: cluster: {
+                mapAttrs (configName: cluster: {
                   what = "fleet cluster configuration";
 
                   children =
-                    builtins.mapAttrs (hostName: host: {
+                    mapAttrs (hostName: host: {
                       what = "host [${host.system}]";
                     })
                     cluster.config.hosts;
@@ -70,19 +69,20 @@
         pkgs,
         ...
       }: let
+        inherit (lib) mapAttrs' elem;
         # Can also be built for darwin, through it is not usual to deploy nixos systems from macos machines.
         # I have no hardware for such testing, thus only adding machines I actually have and use.
         #
         # It is not possible to deploy any host from armv6/armv7 hardware, and I don't think it even makes sense.
         deployerSystems = ["aarch64-linux" "x86_64-linux"];
-        deployerSystem = builtins.elem system deployerSystems;
+        deployerSystem = elem system deployerSystems;
         lib = pkgs.lib;
         rust = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
         craneLib = (crane.mkLib pkgs).overrideToolchain rust;
       in {
-        _module.args.pkgs = import nixpkgs {
+        _module.args.pkgs = import inputs.nixpkgs {
           inherit system;
-          overlays = [(rust-overlay.overlays.default)];
+          overlays = [(inputs.rust-overlay.overlays.default)];
         };
         # Reference fleet package should be built with nightly rust, specified in rust-toolchain.toml.
         packages = lib.mkIf deployerSystem (let
@@ -116,14 +116,14 @@
         checks = let
           packages = import ./pkgs {
             inherit (pkgs) callPackage;
-            craneLib = crane.mkLib (import nixpkgs {inherit system;});
+            craneLib = crane.mkLib pkgs;
           };
           packages-with-nixpkgs-stable = import ./pkgs {
             inherit (pkgs) callPackage;
-            craneLib = crane.mkLib (import nixpkgs-stable-for-tests {inherit system;});
+            craneLib = crane.mkLib (import inputs.nixpkgs-stable-for-tests {inherit system;});
           };
           prefixAttrs = prefix: attrs:
-            nixpkgs.lib.attrsets.mapAttrs' (name: value: {
+            mapAttrs' (name: value: {
               name = "${prefix}${name}";
               value = value.overrideAttrs (prev: {
                 pname = "${prefix}${prev.pname}";
modifiedlib/fleetLib.nixdiffbeforeafterboth
--- a/lib/fleetLib.nix
+++ b/lib/fleetLib.nix
@@ -2,8 +2,11 @@
 {
   nixpkgs,
   hostNames,
-}:
-with nixpkgs.lib; rec {
+}: let
+  inherit (nixpkgs) lib;
+  inherit (lib) listToAttrs remove unique crossLists sort elemAt mkOptionType mkOverride optionalString;
+  inherit (lib.types) listOf coercedTo oneOf submodule;
+in rec {
   hostsToAttrs = f:
     listToAttrs (
       map (name: {
@@ -34,6 +37,27 @@
     then "${this}-${other}"
     else "${other}-${this}";
 
+  types = rec {
+    anyModule = mkOptionType {
+      name = "submodule";
+      inherit (submodule {}) check;
+      merge = lib.options.mergeOneOption;
+      description = "Nixos module";
+    };
+    listOfAnyModuleStrict =
+      listOf anyModule;
+    listOfAnyModule =
+      coercedTo (oneOf [listOfAnyModuleStrict anyModule]) (
+        v:
+          if builtins.isAttrs v
+          then [v]
+          else if builtins.isFunction v
+          then [v]
+          else v
+      )
+      listOfAnyModuleStrict;
+  };
+
   # mkDefault = mkOverride 1000
   # For places, where fleet knows better than nixpkgs defaults.
   mkFleetDefault = mkOverride 999;
modifiedmodules/fleet/assertions.nixdiffbeforeafterboth
--- a/modules/fleet/assertions.nix
+++ b/modules/fleet/assertions.nix
@@ -1,8 +1,10 @@
-{lib, ...}:
-with lib; {
+{lib, ...}: let
+  inherit (lib) mkOption;
+  inherit (lib.types) listOf unspecified str;
+in {
   options = {
     assertions = mkOption {
-      type = types.listOf types.unspecified;
+      type = listOf unspecified;
       internal = true;
       default = [];
       example = [
@@ -21,7 +23,7 @@
     warnings = mkOption {
       internal = true;
       default = [];
-      type = types.listOf types.str;
+      type = listOf str;
       example = ["The `foo' service is deprecated and will go away soon!"];
       description = ''
         This option allows modules to show warnings to users during
modifiedmodules/fleet/meta.nixdiffbeforeafterboth
--- a/modules/fleet/meta.nix
+++ b/modules/fleet/meta.nix
@@ -4,58 +4,53 @@
   config,
   nixpkgs,
   ...
-}:
-with lib;
-with fleetLib; let
-  hostModule = with types;
-    {...} @ hostConfig: let
-      hostName = hostConfig.config._module.args.name;
-    in {
-      options = {
-        nixosModules = mkOption {
-          type = listOf (mkOptionType {
-            name = "submodule";
-            inherit (submodule {}) check;
-            merge = lib.options.mergeOneOption;
-            description = "Nixos module";
-          });
-          description = "List of nixos modules";
-          default = [];
-        };
-        system = mkOption {
-          type = str;
-          description = "Type of system";
-        };
-        encryptionKey = mkOption {
-          type = str;
-          description = "Encryption key";
-        };
-        nixosSystem = mkOption {
-          type = unspecified;
-          description = "Nixos configuration";
-        };
-        nixpkgs = mkOption {
-          type = unspecified;
-          description = "Nixpkgs override";
-          default = nixpkgs;
-        };
+}: let
+  inherit (fleetLib) hostsToAttrs mkFleetGeneratorDefault;
+  inherit (fleetLib.types) listOfAnyModule;
+  inherit (lib) mkOption mkOptionType;
+  inherit (lib.types) str unspecified attrsOf listOf submodule;
+  hostModule = {...} @ hostConfig: let
+    hostName = hostConfig.config._module.args.name;
+  in {
+    options = {
+      nixosModules = mkOption {
+        # Not too strict, but nixos module system will fix everything.
+        type =
+          listOfAnyModule;
+
+        description = "List of nixos modules";
+        default = [];
+      };
+      system = mkOption {
+        type = str;
+        description = "Type of system";
+      };
+      encryptionKey = mkOption {
+        type = str;
+        description = "Encryption key";
+      };
+      nixosSystem = mkOption {
+        type = unspecified;
+        description = "Nixos configuration";
+      };
+      nixpkgs = mkOption {
+        type = unspecified;
+        description = "Nixpkgs override";
+        default = nixpkgs;
       };
-      config = {
-        nixosSystem = hostConfig.config.nixpkgs.lib.nixosSystem {
-          inherit (hostConfig.config) system;
-          modules = hostConfig.config.nixosModules;
-          specialArgs = {
-            inherit fleetLib;
-            fleet = hostsToAttrs (host: config.hosts.${host}.nixosSystem.config);
-          };
+    };
+    config = {
+      nixosSystem = hostConfig.config.nixpkgs.lib.nixosSystem {
+        inherit (hostConfig.config) system;
+        modules = hostConfig.config.nixosModules;
+        specialArgs = {
+          inherit fleetLib;
+          fleet = hostsToAttrs (host: config.hosts.${host}.nixosSystem.config);
         };
-        nixosModules = [
-          ({...}: {
-            networking.hostName = mkFleetGeneratorDefault hostName;
-          })
-        ];
       };
+      nixosModules.networking.hostName = mkFleetGeneratorDefault hostName;
     };
+  };
   overlayType = mkOptionType {
     name = "nixpkgs-overlay";
     description = "nixpkgs overlay";
@@ -63,19 +58,14 @@
     merge = lib.mergeOneOption;
   };
 in {
-  options = with types; {
+  options = {
     hosts = mkOption {
       type = attrsOf (submodule hostModule);
       default = {};
       description = "Configurations of individual hosts";
     };
     nixosModules = mkOption {
-      type = listOf (mkOptionType {
-        name = "submodule";
-        inherit (submodule {}) check;
-        merge = lib.options.mergeOneOption;
-        description = "Nixos modules";
-      });
+      type = listOfAnyModule;
       description = "Modules, which should be added to every system";
       default = [];
     };
@@ -89,9 +79,9 @@
       nixosModules =
         config.nixosModules
         ++ [
-          ({...}: {
+          {
             nixpkgs.overlays = config.overlays;
-          })
+          }
         ];
     });
     nixosModules = import ../../nixos/modules/module-list.nix;
modifiedmodules/fleet/secrets.nixdiffbeforeafterboth
before · modules/fleet/secrets.nix
1{2  lib,3  fleetLib,4  config,5  ...6}:7with lib;8with fleetLib; let9  sharedSecret = with types; ({config, ...}: {10    freeformType = types.lazyAttrsOf unspecified;11    options = {12      expectedOwners = mkOption {13        type = nullOr (listOf str);14        description = ''15          List of hosts to encrypt secret for. null if managed by user (= via owners field from fleet.nix)1617          Secrets would be decrypted and stored to /run/secrets/$\{name} on owners18        '';19        default = null;20      };21      # TODO: Aren't those options may be just desugared to data/expectedData?22      regenerateOnOwnerAdded = mkOption {23        type = bool;24        description = ''25          Is this secret owner-dependent, and needs to be regenerated on ownership set change, or it may be just reencrypted.2627          You want to have this option set to true, when this secret contains some reference to its owners, i.e x509 SANs.28        '';29      };30      regenerateOnOwnerRemoved = mkOption {31        default = config.regenerateOnOwnerAdded;32        type = bool;33        description = ''34          Should this secret be removed on owner removal, or it may be just reencrypted3536          Most probably its value should be equal to regenerateOnOwnerAdded, override only if you know what are you doing.37          Contrary to regenerateOnOwnerAdded, you may want to set this option to false, when host permissions are revoked38          in some other way than by this secret ownership, I.e by firewall/etc.39        '';40      };41      generator = mkOption {42        type = nullOr unspecified;43        description = "Derivation to evaluate for secret generation";44        default = null;45      };46      createdAt = mkOption {47        type = nullOr str;48        description = "When this secret was (re)generated";49        default = null;50      };51      expiresAt = mkOption {52        type = nullOr str;53        description = "On which date this secret will expire, someone should regenerate this secret before it expires.";54        default = null;55      };5657      owners = mkOption {58        type = listOf str;59        description = ''60          For which owners this secret is currently encrypted,61          if not matches expectedOwners - then this secret is considered outdated, and62          should be regenerated/reencrypted.6364          Imported from fleet.nix65        '';66        default = [];67      };68    };69  });70  hostSecret = with types; {71    freeformType = types.lazyAttrsOf unspecified;72    options = {73      createdAt = mkOption {74        type = nullOr str;75        default = null;76      };77      expiresAt = mkOption {78        type = nullOr str;79        default = null;80      };81    };82  };83in {84  options = with types; {85    version = mkOption {86      type = str;87      default = "";88      internal = true;89    };90    sharedSecrets = mkOption {91      type = attrsOf (submodule sharedSecret);92      default = {};93      description = "Shared secrets";94    };95    hostSecrets = mkOption {96      type = attrsOf (attrsOf (submodule hostSecret));97      default = {};98      description = "Host secrets. Imported from fleet.nix";99      internal = true;100    };101  };102  config = {103    assertions =104      mapAttrsToList105      (name: secret: {106        assertion = secret.expectedOwners == null || builtins.sort (a: b: a < b) secret.owners == builtins.sort (a: b: a < b) secret.expectedOwners;107        message = "Shared secret ${name} is expected to be encrypted for ${builtins.toJSON secret.expectedOwners}, but it is encrypted for ${builtins.toJSON secret.owners}. Run fleet secrets regenerate to fix";108      })109      config.sharedSecrets;110    hosts = hostsToAttrs (host: {111      nixosModules = let112        # processPart113        processSecret = v:114          (removeAttrs v ["createdAt" "expiresAt" "expectedOwners" "owners" "regenerateOnOwnerAdded" "regenerateOnOwnerRemoved"])115          // {116            shared = true;117          };118      in [119        {120          secrets =121            (122              mapAttrs (_: processSecret)123              (filterAttrs (_: v: builtins.elem host v.owners) config.sharedSecrets)124            )125            // (mapAttrs (_: processSecret) (config.hostSecrets.${host} or {}));126        }127      ];128    });129    # TODO: Should this attribute be moved to `nixpkgs.overlays`?130    overlays = [131      (final: prev: let132        lib = final.lib;133        inherit (lib) strings;134        inherit (strings) concatStringsSep;135      in {136        mkSecretGenerators = {recipients}: rec {137          # TODO: Merge both generators to one with consistent options syntax?138          # Impure generator is built on local machine, then built closure is copied to remote machine,139          # and then it is ran in inpure context, so that this generator may access HSMs and other things.140          mkImpureSecretGenerator = {141            script,142            # If set - script will be run on remote machine, otherwise it will be run with fleet project in CWD143            # (Some secrets-encryption-in-git/managed PKI solution is expected)144            impureOn ? null,145          }:146            (prev.writeShellScript "impureGenerator.sh" ''147              #!/bin/sh148              set -eu149150              export GENERATOR_HELPER_IDENTITIES="${concatStringsSep "\n" recipients}";151              export PATH=${final.fleet-generator-helper}/bin:$PATH152153              # TODO: Provide tempdir from outside, to make it securely erasurable as needed?154              tmp=$(mktemp -d)155              cd $tmp156              # cd /var/empty157158              created_at=$(date -u +"%Y-%m-%dT%H:%M:%S.%NZ")159160              ${script}161162              if ! test -d $out; then163                echo "impure generator script did not produce expected \$out output"164                exit 1165              fi166167              echo -n $created_at > $out/created_at168              echo -n SUCCESS > $out/marker169            '')170            .overrideAttrs (old: {171              passthru = {172                inherit impureOn;173                generatorKind = "impure";174              };175            });176          # Pure generators are disabled for now177          mkSecretGenerator = {script}: mkImpureSecretGenerator {inherit script;};178179          # TODO: Implement consistent naming180          # Pure secret generator is supposed to be run entirely by nix, using `__impure` derivation type...181          # But for now, it is ran the same way as `impureSecretGenerator`, but on the local machine.182          # mkSecretGenerator = {script}:183          #   (prev.writeShellScript "generator.sh" ''184          #     #!/bin/sh185          #     set -eu186          #     # TODO: make nix daemon build secret, not just the script.187          #     cd /var/empty188          #189          #     created_at=$(date -u +"%Y-%m-%dT%H:%M:%S.%NZ")190          #191          #     ${script}192          #     if ! test -d $out; then193          #       echo "impure generator script did not produce expected \$out output"194          #       exit 1195          #     fi196          #197          #     echo -n $created_at > $out/created_at198          #     echo -n SUCCESS > $out/marker199          #   '')200          #   .overrideAttrs (old: {201          #     passthru = {202          #       generatorKind = "pure";203          #     };204          #     # TODO: make nix daemon build secret, not just the script.205          #     # __impure = true;206          #   });207        };208      })209    ];210  };211}
modifiednixos/meta.nixdiffbeforeafterboth
--- a/nixos/meta.nix
+++ b/nixos/meta.nix
@@ -2,11 +2,13 @@
   lib,
   pkgs,
   ...
-}:
-with lib; {
-  options = with types; {
+}: let
+  inherit (lib) mkOption;
+  inherit (lib.types) listOf str submodule;
+in {
+  options = {
     nixpkgs.resolvedPkgs = mkOption {
-      type = types.pkgs // {description = "nixpkgs.pkgs";};
+      type = lib.types.pkgs // {description = "nixpkgs.pkgs";};
       description = "Value of pkgs";
     };
     tags = mkOption {
@@ -30,9 +32,6 @@
         };
       };
       description = "Network definition of host";
-    };
-    buildTarget = mkOption {
-      type = enum ["toplevel" "sd-image" "installation-cd"];
     };
   };
   config = {
modifiednixos/secrets.nixdiffbeforeafterboth
--- a/nixos/secrets.nix
+++ b/nixos/secrets.nix
@@ -3,16 +3,17 @@
   config,
   pkgs,
   ...
-}:
-with lib; let
+}: let
   inherit (lib.strings) hasPrefix removePrefix;
+  inherit (lib) mkOption mkOptionDefault mapAttrs stringAfter;
+  inherit (lib.types) submodule str attrsOf nullOr unspecified lazyAttrsOf;
   plaintextPrefix = "<PLAINTEXT>";
   plaintextNewlinePrefix = "<PLAINTEXT-NL>";
 
   sysConfig = config;
   secretPartType = secretName:
-    types.submodule ({config, ...}: {
-      options = with types; {
+    submodule ({config, ...}: {
+      options = {
         raw = mkOption {
           description = "Secret in fleet-specific undocumented format, do not use. Import from fleet.nix";
           internal = true;
@@ -49,11 +50,11 @@
         stablePath = mkOptionDefault "/run/secrets/${secretName}/${partName}";
       };
     });
-  secretType = types.submodule ({config, ...}: let
+  secretType = submodule ({config, ...}: let
     secretName = config._module.args.name;
   in {
-    freeformType = types.lazyAttrsOf (secretPartType secretName);
-    options = with types; {
+    freeformType = lazyAttrsOf (secretPartType secretName);
+    options = {
       shared = mkOption {
         description = "Is this secret owned by this machine, or propagated from shared secrets";
         default = false;
@@ -112,7 +113,7 @@
 in {
   options = {
     secrets = mkOption {
-      type = types.attrsOf secretType;
+      type = attrsOf secretType;
       default = {};
       description = "Host-local secrets";
     };