{ lib, config, options, ... }: let inherit (lib) mkOption types; inherit (lib.types) attrsOf attrTag deferredModuleWith submodule submoduleWith optionType functionTo raw ; functionType = import ./function.nix; configuration = mkOption { description = "Configuration type declaring options to be set by operators"; type = optionType; readOnly = true; default = submoduleWith { class = "fediversity-configuration"; modules = [ { options = { enable = lib.mkEnableOption { description = "your Fediversity configuration"; }; applications = lib.mapAttrs ( _name: application: mkOption { description = application.description; type = submoduleWith { class = "fediversity-application-config"; modules = [ application.module ]; }; default = { }; } ) config.applications; }; } ]; }; }; in { _class = "fediversity-settings"; options = { inherit configuration; resources = mkOption { description = "Collection of deployment resources that can be required by applications and policed by hosting providers"; 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 = submoduleWith { class = "fediversity-resource-policy"; modules = [ (policy: { _class = "fediversity-resource-policy"; options = { # 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. 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? apply = mkOption { description = "Apply the policy to a request"; type = functionTo policy.config.resource-type; }; }; }) ]; }; }; }; } ) ]; }); }; applications = mkOption { description = "Collection of Fediversity applications"; type = attrsOf (submoduleWith { class = "fediversity-application"; modules = [ { 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"; } ]; }; }; resources = mkOption { description = "Mapping of application configuration to deployment resources, a description of what an application needs to run"; # TODO: maybe transpose, and group the resources by type instead type = functionTo ( attrsOf ( attrTag ( lib.mapAttrs ( _name: resource: mkOption { type = types.submodule resource.request; } ) config.resources ) ) ); }; }; } ]; }); }; environments = mkOption { description = "Run-time environments for Fediversity applications to be deployed to"; 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 = (lib.evalModules { modules = options.resources.type.nestedTypes.elemType.getSubModules ; }).options.policy.type.getSubModules ++ [ options.resources.value.${name} ]; }; } ) config.resources ) ); }; implementation = mkOption { description = "Mapping of resources required by applications to available resources; the result can be deployed"; type = functionTo environment.config.resource-mapping.output-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 = submodule configuration; output-type = raw; }; }; 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); }; }; }) ]; }); }; }; }