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; + }; + }; +}