diff options
-rw-r--r-- | machines/profpatsch/base-server-options.toml | 8 | ||||
-rw-r--r-- | machines/profpatsch/base-server.nix | 51 | ||||
-rw-r--r-- | machines/profpatsch/base-server.toml | 26 | ||||
-rw-r--r-- | pkgs/profpatsch/nixos-toml-modules.nix | 119 |
4 files changed, 182 insertions, 22 deletions
diff --git a/machines/profpatsch/base-server-options.toml b/machines/profpatsch/base-server-options.toml new file mode 100644 index 00000000..3cfc6e51 --- /dev/null +++ b/machines/profpatsch/base-server-options.toml @@ -0,0 +1,8 @@ +[module] +type = "nixos-options" +version = "0.0.1" + +[vuizvui.user.profpatsch.server.sshPort] +description = "ssh port" +type = "port" +default = 6879 diff --git a/machines/profpatsch/base-server.nix b/machines/profpatsch/base-server.nix index 921e5d8d..7b0a714a 100644 --- a/machines/profpatsch/base-server.nix +++ b/machines/profpatsch/base-server.nix @@ -1,36 +1,43 @@ { config, pkgs, lib, ... }: let - cfg = config.vuizvui.user.profpatsch.server; + cfgImports = (import ../../pkgs/profpatsch/nixos-toml-modules.nix { inherit lib; }).readAnyToml ./base-server.toml + config; in { - imports = [ - ./base.nix - ]; + inherit (cfgImports) imports; - options.vuizvui.user.profpatsch.server.sshPort = lib.mkOption { - description = "ssh port"; - # TODO: replace with types.intBetween https://github.com/NixOS/nixpkgs/pull/27239 - type = with lib.types; addCheck int (x: x >= 0 && x <= 65535); - default = 6879; - }; + # TODO: cannot read options from pkgs because it would lead to an infinite recursion + # in the module system, since the pkgs passed into this module already requires all options. + options = ((import ../../pkgs/profpatsch/nixos-toml-modules.nix { inherit lib; }).readAnyToml ./base-server-options.toml).options + ; - config = { + config = cfgImports.config; - programs.mosh.enable = true; - services.openssh = { - enable = true; - listenAddresses = [ { addr = "0.0.0.0"; port = cfg.sshPort; } ]; - }; + # options.vuizvui.user.profpatsch.server.sshPort = lib.traceValSeqN 3 (lib.mkOption { + # description = "ssh port"; + # # TODO: replace with types.intBetween https://github.com/NixOS/nixpkgs/pull/27239 + # type = with lib.types; addCheck int (x: x >= 0 && x <= 65535); + # default = 6879; + # }); - networking.firewall = { - enable = true; - allowPing = true; - allowedTCPPorts = [ cfg.sshPort ]; - }; + # config = { - }; + # programs.mosh.enable = true; + + # services.openssh = { + # enable = true; + # listenAddresses = [ { addr = "0.0.0.0"; port = cfg.sshPort; } ]; + # }; + + # networking.firewall = { + # enable = true; + # allowPing = true; + # allowedTCPPorts = [ cfg.sshPort ]; + # }; + + # }; } diff --git a/machines/profpatsch/base-server.toml b/machines/profpatsch/base-server.toml new file mode 100644 index 00000000..b2b4a88a --- /dev/null +++ b/machines/profpatsch/base-server.toml @@ -0,0 +1,26 @@ +[module] +type = "nixos-config" +version = "0.0.1" + +[[imports]] +module = "./base.nix" + +[configVariables] +server = [ "vuizvui", "user", "profpatsch", "server" ] + +[programs.mosh] +enable = true + +[services.openssh] +enable = true + + [[services.openssh.listenAddresses]] + addr = "0.0.0.0" + port._configVariable.server = "sshPort" + +[networking.firewall] +enable = true +allowPing = true + +[[networking.firewall.allowedTCPPorts]] +_configVariable.server = "sshPort" diff --git a/pkgs/profpatsch/nixos-toml-modules.nix b/pkgs/profpatsch/nixos-toml-modules.nix new file mode 100644 index 00000000..1424292a --- /dev/null +++ b/pkgs/profpatsch/nixos-toml-modules.nix @@ -0,0 +1,119 @@ +{ lib }: + +let + tv = lib.traceValSeqN 3; + parseSemver = s: + let els = lib.splitString "." s; + in assert (lib.assertMsg (builtins.length els == 3) "semver must be of the form maj.min.patch, was ${s}"); { + major = lib.head els; + minor = lib.head (lib.tail els); + patch = lib.head (lib.tail (lib.tail els)); + }; + semver = major: minor: patch: { inherit major minor patch; }; + + nixos-options-0_0_1 = {toml, tomlFileDir}: + let + toOption = { description, type, default }: lib.mkOption { + inherit description default; + # TODO: this only works for types which are not functions obviously + type = lib.types.${type}; + }; + + transformOptions = + let isOptionAttrset = a: a ? description && a ? type && a ? default; + in lib.mapAttrsRecursiveCond + # TODO: if an option set had these fields for any reason, + # we wouldn’t recognize it as an option. + # Maybe there should be an escape hatch in the toml description? + # We wouldn’t want the toml description to need an extra annotation for an option definition + (attrs: !isOptionAttrset attrs) + (_path: attrs: + if isOptionAttrset attrs + then toOption attrs + else lib.id); + + in { + options = transformOptions toml; + }; + + nixos-config-0_0_1 = {toml, tomlFileDir } : config: + let + configVariables = toml.configVariables or {}; + getSingleAttr = attrs: errWrongLength: + let keys = lib.attrNames attrs; + len = lib.length keys; + in + assert lib.assertMsg (len == 1) + (errWrongLength { num = len; keys = keys; }); + let key = lib.head keys; + in { name = key; value = attrs.${key}; }; + mapIfAttrs = attrKeys: f: attrs: + # lib.attrNames is always sorted like (lib.sort lib.lessThan) + if (lib.sort lib.lessThan attrKeys) == (lib.attrNames attrs) + then { mapped = true; val = f attrs; } + else { mapped = false; val = attrs; }; + + transformConfigVariable = { _configVariable }: + let var = getSingleAttr _configVariable + ({ num, keys }: "_configVariable must have exactly one attribute, the config variable to match, but you gave ${toString num}: ${lib.generators.toPretty {} keys}"); + in + assert lib.assertMsg (configVariables ? ${var.name}) + "_configVariable referenced ${var.name}, but you haven’t specified ${var.name} in [configVariables], only: ${lib.generators.toPretty {} toml.configVariables}"; + let + initialPath = configVariables.${var.name}; + lastPathElem = var.value; + in lib.getAttrFromPath (initialPath ++ [lastPathElem]) config; + + relativeStringToPath = parentPath: relString: parentPath + ("/" + relString); + transformImports = map ({module}: relativeStringToPath tomlFileDir module); + + # recurse a structure of attrsets and lists recursively. + # f takes the current value and returns a { recurse : bool, val : a} + # where if recurse is true the result of f (val) will be recursed into further + # and val is the returned value. + mapValRecursive = f: val: + let mapped = f val; + in if !mapped.recurse then mapped.val + else if lib.isAttrs mapped.val + then lib.mapAttrs (_: mapValRecursive f) mapped.val + else if lib.isList mapped.val + then map (mapValRecursive f) mapped.val + else mapped.val; + + transformConfig = + { imports = transformImports toml.imports; + config = + mapValRecursive + (val: + if lib.isAttrs val + then + let m = mapIfAttrs [ "_configVariable" ] transformConfigVariable val; + in { recurse = !m.mapped; val = m.val; } + else { recurse = true; inherit val; }) + (removeAttrs toml [ "imports" "configVariables" ]); + }; + + in transformConfig; + + readAnyToml = filePath: + let + toml = builtins.fromTOML (builtins.readFile filePath); + parsers = { + "nixos-options" = { + "0.0.1" = nixos-options-0_0_1; + }; + "nixos-config" = { + "0.0.1" = nixos-config-0_0_1; + }; + }; + # TODO: these errors are gonna be horrible … + in parsers.${toml.module.type}.${toml.module.version} { + toml = (removeAttrs toml [ "module" ]); + tomlFileDir = builtins.dirOf filePath; + }; + +in { + inherit + readAnyToml + ; +} |