From b13d00cff00deb004e88d843dd0dc75f34ec5ba4 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 17 Aug 2025 23:17:55 +0200 Subject: [PATCH] factor common logic back out note that i worked around the question of how to delegate part of the options to the consumer by creating aliases for different parts of the function slot filling. i've been pretty arbitrary about this so far tho, and mostly to preserve existing interfaces, rather than out of conscious UX design per se. so the interface can def change still, but at least the _user_'s side is more DRY now! --- deployment/data-model-test.nix | 6 +- deployment/data-model.nix | 170 ++++++++++----------------------- deployment/function.nix | 58 +++++++++++ 3 files changed, 111 insertions(+), 123 deletions(-) create mode 100644 deployment/function.nix diff --git a/deployment/data-model-test.nix b/deployment/data-model-test.nix index 5bd58b21..7ce2fce2 100644 --- a/deployment/data-model-test.nix +++ b/deployment/data-model-test.nix @@ -99,7 +99,7 @@ in { options.enable = lib.mkEnableOption "Hello in the shell"; }; - config-mapping.implementation = + implementation = cfg: lib.optionalAttrs cfg.enable { resources.hello.login-shell.packages.hello = pkgs.hello; @@ -109,7 +109,7 @@ in { config, ... }: { resources.operator-environment.login-shell.username = "operator"; - resource-mapping.implementation = + implementation = requests: { providers, ... }: { @@ -149,7 +149,7 @@ in }; } ); - resources = fediversity.applications.hello.config-mapping.apply fediversity.example-configuration.applications.hello; + resources = fediversity.applications.hello.resources fediversity.example-configuration.applications.hello; hello-shell = resources.resources.hello.login-shell; environment = fediversity.environments.single-nixos-vm.resources.operator-environment.login-shell; result = mkDeployment { diff --git a/deployment/data-model.nix b/deployment/data-model.nix index 9620e3ea..c6fcfbd4 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -15,6 +15,7 @@ let functionTo ; + functionType = import ./function.nix; application-resources = submodule { options.resources = mkOption { # TODO: maybe transpose, and group the resources by type instead @@ -96,63 +97,26 @@ in description = "Operator-facing configuration options for the application"; type = deferredModuleWith { staticModules = [ { _class = "fediversity-application-config"; } ]; }; }; + implementation = mkOption { + description = "Mapping of application configuration to deployment resources, a description of what an application needs to run"; + type = application.config.config-mapping.function-type; + }; + resources = mkOption { + description = "Compute resources required by an application"; + type = application.config.config-mapping.function-type; + readOnly = true; + default = application.config.config-mapping.apply; + }; # TODO(@fricklerhandwerk): this needs a better name config-mapping = mkOption { - description = "Function type for the mapping from application configuration to required resources"; - type = submodule ( - { config, ... }: - { - options = { - input-type = mkOption { - type = optionType; - default = submodule application.config.module; - # default = types.int; - }; - output-type = mkOption { - type = optionType; - default = application-resources; - }; - function-type = mkOption { - type = optionType; - readOnly = true; - default = functionTo config.output-type; - }; - wrapper-type = mkOption { - type = optionType; - readOnly = true; - default = functionTo (submodule { - options = { - input = mkOption { - type = config.input-type; - }; - output = mkOption { - type = config.output-type; - }; - }; - }); - }; - implementation = mkOption { - type = config.function-type; - description = "Mapping of application configuration to deployment resources, a description of what an application needs to run"; - default = _: { }; - }; - wrapper = mkOption { - type = config.wrapper-type; - readOnly = true; - default = input: fn: { - inherit input; - output = config.implementation fn.config.input; - }; - }; - apply = mkOption { - type = config.function-type; - readOnly = true; - default = input: (config.wrapper input).output; - }; - }; - } - ); - default = { }; + description = "Compute resources required by an application"; + type = submodule functionType; + readOnly = true; + default = { + input-type = submodule application.config.module; + output-type = application-resources; + implementation = application.config.implementation; + }; }; }; }) @@ -177,80 +141,46 @@ in ) ); }; + implementation = mkOption { + description = "Mapping of resources required by applications to available resources; the result can be deployed"; + type = environment.config.resource-mapping.function-type; + }; resource-mapping = mkOption { description = "Function type for the mapping from resources to a (NixOps4) deployment"; - type = submodule ( - { config, ... }: - { - options = { - input-type = mkOption { - type = optionType; - default = submodule application-resources; - # default = types.int; - }; - output-type = mkOption { - type = optionType; - default = nixops4Deployment; - }; - function-type = mkOption { - type = optionType; - readOnly = true; - default = functionTo config.output-type; - }; - wrapper-type = mkOption { - type = optionType; - readOnly = true; - default = functionTo (submodule { - options = { - input = mkOption { - type = config.input-type; - }; - output = mkOption { - type = config.output-type; - }; - }; - }); - }; - implementation = mkOption { - type = config.function-type; - description = "Mapping of resources required by applications to available resources; the result can be deployed"; - default = _: { }; - }; - wrapper = mkOption { - type = config.wrapper-type; - readOnly = true; - default = input: fn: { - inherit input; - output = config.implementation fn.config.input; - }; - }; - apply = mkOption { - type = config.function-type; - readOnly = true; - default = input: (config.wrapper input).output; - }; - }; - } - ); - default = { }; + type = submodule functionType; + readOnly = true; + default = { + input-type = application-resources; + output-type = nixops4Deployment; + implementation = environment.config.implementation; + }; + }; + config-mapping = mkOption { + description = "Mapping from a configuration to a deployment"; + type = submodule functionType; + readOnly = true; + default = { + input-type = config.configuration; + output-type = nixops4Deployment; + implementation = + cfg: + # TODO: check cfg.enable.true + let + required-resources = lib.mapAttrs ( + name: application-settings: config.applications.${name}.resources application-settings + ) cfg.applications; + in + environment.config.resource-mapping.apply required-resources; + }; }; # TODO(@fricklerhandwerk): maybe this should be a separate thing such as `fediversity-setup`, # which makes explicit which applications and environments are available. # then the deployments can simply be the result of the function application baked into this module. deployment = mkOption { description = "Generate a deployment from a configuration, by applying an environment's resource policies to the applications' resource mappings"; - type = functionTo (environment.config.resource-mapping.output-type); + type = environment.config.config-mapping.function-type; readOnly = true; - default = - cfg: - # TODO: check cfg.enable.true - let - required-resources = lib.mapAttrs ( - name: application-settings: config.applications.${name}.resources application-settings - ) cfg.applications; - in - environment.config.implementation required-resources; - + default = environment.config.config-mapping.apply; }; }; }) diff --git a/deployment/function.nix b/deployment/function.nix new file mode 100644 index 00000000..76795e68 --- /dev/null +++ b/deployment/function.nix @@ -0,0 +1,58 @@ +/** + Modular function type +*/ +{ config, lib, ... }: +let + inherit (lib) mkOption types; + inherit (types) + submodule + functionTo + optionType + ; +in +{ + options = { + input-type = mkOption { + type = optionType; + }; + output-type = mkOption { + type = optionType; + }; + function-type = mkOption { + type = optionType; + readOnly = true; + default = functionTo config.output-type; + }; + wrapper-type = mkOption { + type = optionType; + readOnly = true; + default = functionTo (submodule { + options = { + input = mkOption { + type = config.input-type; + }; + output = mkOption { + type = config.output-type; + }; + }; + }); + }; + implementation = mkOption { + type = config.function-type; + default = _: { }; + }; + wrapper = mkOption { + type = config.wrapper-type; + readOnly = true; + default = input: fn: { + inherit input; + output = config.implementation fn.config.input; + }; + }; + apply = mkOption { + type = config.function-type; + readOnly = true; + default = input: (config.wrapper input).output; + }; + }; +}