From 8cf73404fb62eefc7907d6e93ed37b4e980191d4 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Thu, 31 Jul 2025 16:50:36 +0200 Subject: [PATCH] add classes note: resource policy has remained as to use `deferredModuleWith` (rather than `submoduleWith`) to ensure evaluation will go thru. --- deployment/data-model-test.nix | 2 + deployment/data-model.nix | 264 ++++++++++++++++++--------------- 2 files changed, 147 insertions(+), 119 deletions(-) diff --git a/deployment/data-model-test.nix b/deployment/data-model-test.nix index 7c11f687..8a500321 100644 --- a/deployment/data-model-test.nix +++ b/deployment/data-model-test.nix @@ -17,6 +17,8 @@ let inherit (inputs.nixops4.lib) mkDeployment; in { + _class = "nix-unit"; + test-eval = { expr = let diff --git a/deployment/data-model.nix b/deployment/data-model.nix index 10256d30..841ebcb8 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -11,12 +11,14 @@ let attrTag deferredModuleWith submodule + submoduleWith optionType functionTo ; functionType = import ./function.nix; application-resources = submodule { + _class = "fediversity-application-requirements"; options.resources = mkOption { # TODO: maybe transpose, and group the resources by type instead type = attrsOf ( @@ -40,140 +42,161 @@ let }; in { + _class = "fediversity-settings"; options = { resources = mkOption { description = "Collection of deployment resources that can be required by applications and policed by hosting providers"; - type = attrsOf ( - submodule ( - { ... }: - { - _class = "fediversity-resource"; - options = { - description = mkOption { - description = "Description of the resource to help application module authors and hosting providers to work with it"; - type = types.str; - }; - request = mkOption { - description = "Options for declaring resource requirements by an application, a description of how the resource is consumed or accessed"; - type = deferredModuleWith { staticModules = [ { _class = "fediversity-resource-request"; } ]; }; - }; - policy = mkOption { - description = "Options for configuring the resource policy for the hosting provider, a description of how the resource is made available"; - type = deferredModuleWith { - staticModules = [ - (policy: { - _class = "fediversity-resource-policy"; - # TODO(@fricklerhandwerk): not sure it can be made - # sensible syntactically, but essentially we want to - # ensure that `apply` is defined, but since its output - # depends on the specific policy we also need to - # determine that somehow. - # hopefully this also helps with correct composition down the line. - options.resource-type = mkOption { - description = "The type of resource this policy configures"; - type = types.optionType; - }; - # TODO(@fricklerhandwerk): do we need a function type here as well, or is it in the way? - options.apply = mkOption { - description = "Apply the policy to a request"; - type = with types; functionTo policy.config.resource-type; - }; - }) - ]; + type = attrsOf (submoduleWith { + class = "fediversity-resource"; + modules = [ + ( + { ... }: + { + options = { + description = mkOption { + description = "Description of the resource to help application module authors and hosting providers to work with it"; + type = types.str; + }; + request = mkOption { + description = "Options for declaring resource requirements by an application, a description of how the resource is consumed or accessed"; + type = deferredModuleWith { staticModules = [ { _class = "fediversity-resource-request"; } ]; }; + }; + policy = mkOption { + description = "Options for configuring the resource policy for the hosting provider, a description of how the resource is made available"; + type = deferredModuleWith { + staticModules = [ + (policy: { + _class = "fediversity-resource-policy"; + # TODO(@fricklerhandwerk): not sure it can be made + # sensible syntactically, but essentially we want to + # ensure that `apply` is defined, but since its output + # depends on the specific policy we also need to + # determine that somehow. + # hopefully this also helps with correct composition down the line. + options.resource-type = mkOption { + description = "The type of resource this policy configures"; + type = types.optionType; + }; + # TODO(@fricklerhandwerk): do we need a function type here as well, or is it in the way? + options.apply = mkOption { + description = "Apply the policy to a request"; + type = with types; functionTo policy.config.resource-type; + }; + }) + ]; + }; }; }; - }; - } - ) - ); + } + ) + ]; + }); }; applications = mkOption { description = "Collection of Fediversity applications"; - type = attrsOf ( - submodule (application: { - _class = "fediversity-application"; - options = { - description = mkOption { - description = "Description to be shown in the application overview"; - type = types.str; - }; - module = mkOption { - 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 = functionTo application.config.config-mapping.output-type; - readOnly = true; - default = input: (application.config.implementation input).output; - }; - config-mapping = mkOption { - description = "Function type for the mapping from application configuration to required resources"; - type = submodule functionType; - readOnly = true; - default = { - input-type = submodule application.config.module; - output-type = application-resources; + type = attrsOf (submoduleWith { + class = "fediversity-application"; + modules = [ + (application: { + options = { + description = mkOption { + description = "Description to be shown in the application overview"; + type = types.str; + }; + module = mkOption { + 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 = functionTo application.config.config-mapping.output-type; + readOnly = true; + default = input: (application.config.implementation input).output; + }; + config-mapping = mkOption { + description = "Function type for the mapping from application configuration to required resources"; + type = submoduleWith { + class = "module-function"; + modules = [ functionType ]; + }; + readOnly = true; + default = { + input-type = submodule application.config.module; + output-type = application-resources; + }; }; }; - }; - }) - ); + }) + ]; + }); }; environments = mkOption { description = "Run-time environments for Fediversity applications to be deployed to"; - type = attrsOf ( - submodule (environment: { - _class = "fediversity-environment"; - options = { - resources = mkOption { - description = '' - Resources made available by the hosting provider, and their policies. + type = attrsOf (submoduleWith { + class = "fediversity-environment"; + modules = [ + (environment: { + options = { + resources = mkOption { + description = '' + Resources made available by the hosting provider, and their policies. + + Setting this is optional, but provides a place to declare that information for programmatic use in the resource mapping. + ''; + # TODO: maybe transpose, and group the resources by type instead + type = attrsOf ( + attrTag ( + lib.mapAttrs ( + _name: resource: + mkOption { + type = submoduleWith { + class = "fediversity-resource-policy"; + modules = [ resource.policy ]; + }; + } + ) config.resources + ) + ); + }; + 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 = submoduleWith { + class = "module-function"; + modules = [ functionType ]; + }; + readOnly = true; + default = { + input-type = application-resources; + output-type = nixops4Deployment; + }; + }; + deployment = mkOption { + description = "Generate a deployment from a configuration"; + type = functionTo (environment.config.resource-mapping.output-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).output; - Setting this is optional, but provides a place to declare that information for programmatic use in the resource mapping. - ''; - # TODO: maybe transpose, and group the resources by type instead - type = attrsOf ( - attrTag ( - lib.mapAttrs (_name: resource: mkOption { type = submodule resource.policy; }) config.resources - ) - ); - }; - 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 functionType; - readOnly = true; - default = { - input-type = application-resources; - output-type = nixops4Deployment; }; }; - deployment = mkOption { - description = "Generate a deployment from a configuration"; - type = functionTo (environment.config.resource-mapping.output-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).output; - - }; - }; - }) - ); + }) + ]; + }); }; configuration = mkOption { description = "Configuration type declaring options to be set by operators"; @@ -188,7 +211,10 @@ in _name: application: mkOption { description = application.description; - type = submodule application.module; + type = submoduleWith { + class = "fediversity-application-config"; + modules = [ application.module ]; + }; default = { }; } ) config.applications;