From 9f596ad820db73ca93b15b504394117d4520b30f Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Thu, 31 Jul 2025 17:28:13 +0200 Subject: [PATCH] data model: add classes specifies classes in the data model, raising these to from `_class` model attributes to `subModuleWith`'s `class` parameter where possible. note: resource policy has remained as to use `deferredModuleWith` (rather than `submoduleWith`) to ensure evaluation will go thru. --- deployment/data-model.nix | 266 +++++++++++++++++++++----------------- 1 file changed, 147 insertions(+), 119 deletions(-) diff --git a/deployment/data-model.nix b/deployment/data-model.nix index c3d5d53a..3858fce1 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 ( @@ -31,7 +33,6 @@ let inputs.nixops4.modules.nixops4Deployment.default { - _class = "nixops4Deployment"; _module.args = { resourceProviderSystem = builtins.currentSystem; resources = { }; @@ -41,139 +42,163 @@ 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"; - options.resource-type = mkOption { - description = "The type of resource this policy configures"; - type = types.optionType; - }; - # TODO(@fricklerhandwerk): we may want to make the function type explict here: `request -> resource-type` - # and then also rename this to be consistent with the application's resource mapping - options.apply = mkOption { - description = "Apply the policy to a request"; - type = functionTo policy.config.resource-type; - }; - }) - ]; + type = attrsOf (submoduleWith { + class = "fediversity-resource"; + modules = [ + ( + { ... }: + { + _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"; + options.resource-type = mkOption { + description = "The type of resource this policy configures"; + type = types.optionType; + }; + # TODO(@fricklerhandwerk): we may want to make the function type explict here: `request -> resource-type` + # and then also rename this to be consistent with the application's resource mapping + options.apply = mkOption { + description = "Apply the policy to a request"; + type = 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; - }; - # TODO(@fricklerhandwerk): this needs a better name, it's just the type - 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: { + _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; + }; + # TODO(@fricklerhandwerk): this needs a better name, it's just the type + 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; + }; + }; + # 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); + 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; }; }; - # 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); - 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 +213,10 @@ in _name: application: mkOption { description = application.description; - type = submodule application.module; + type = submoduleWith { + class = "fediversity-application-config"; + modules = [ application.module ]; + }; default = { }; } ) config.applications;