{ lib, config, inputs, ... }: let inherit (lib) mkOption types; inherit (lib.types) attrsOf attrTag deferredModuleWith submodule optionType functionTo ; application-resources = submodule { options.resources = mkOption { # TODO: maybe transpose, and group the resources by type instead type = attrsOf ( attrTag ( lib.mapAttrs (_name: resource: mkOption { type = submodule resource.request; }) config.resources ) ); }; }; nixops4Deployment = types.deferredModuleWith { staticModules = [ inputs.nixops4.modules.nixops4Deployment.default { _class = "nixops4Deployment"; _module.args = { resourceProviderSystem = builtins.currentSystem; resources = { }; }; } ]; }; in { 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; }; }) ]; }; }; }; } ) ); }; applications = mkOption { description = "Collection of Fediversity applications"; type = attrsOf (submodule { _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 required by an application"; # input: submodule application.config.module type = functionTo 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. 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 from resources required by applications to available resources to a deployment"; # input: application-resources type = functionTo 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 nixops4Deployment; 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"; type = optionType; readOnly = true; default = submodule { options = { enable = lib.mkEnableOption { description = "your Fediversity configuration"; }; applications = lib.mapAttrs ( _name: application: mkOption { description = application.description; type = submodule application.module; default = { }; } ) config.applications; }; }; }; }; }