factor common logic back out

note that i worked around the question of how to delegate part of the
options to the consumer by creating aliases for different parts of the
function slot filling.
i've been pretty arbitrary about this so far tho, and mostly to preserve
existing interfaces, rather than out of conscious UX design per se.
so the interface can def change still, but at least the _user_'s side is
more DRY now!
This commit is contained in:
Kiara Grouwstra 2025-08-17 23:17:55 +02:00
parent fb004a4d4c
commit efbe7deddd
Signed by: kiara
SSH key fingerprint: SHA256:COspvLoLJ5WC5rFb9ZDe5urVCkK4LJZOsjfF4duRJFU
3 changed files with 111 additions and 123 deletions

View file

@ -99,7 +99,7 @@ in
{ {
options.enable = lib.mkEnableOption "Hello in the shell"; options.enable = lib.mkEnableOption "Hello in the shell";
}; };
config-mapping.implementation = implementation =
cfg: cfg:
lib.optionalAttrs cfg.enable { lib.optionalAttrs cfg.enable {
resources.hello.login-shell.packages.hello = pkgs.hello; resources.hello.login-shell.packages.hello = pkgs.hello;
@ -109,7 +109,7 @@ in
{ config, ... }: { config, ... }:
{ {
resources.operator-environment.login-shell.username = "operator"; resources.operator-environment.login-shell.username = "operator";
resource-mapping.implementation = implementation =
requests: requests:
{ providers, ... }: { 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; hello-shell = resources.resources.hello.login-shell;
environment = fediversity.environments.single-nixos-vm.resources.operator-environment.login-shell; environment = fediversity.environments.single-nixos-vm.resources.operator-environment.login-shell;
result = mkDeployment { result = mkDeployment {

View file

@ -15,6 +15,7 @@ let
functionTo functionTo
; ;
functionType = import ./function.nix;
application-resources = submodule { application-resources = submodule {
options.resources = mkOption { options.resources = mkOption {
# TODO: maybe transpose, and group the resources by type instead # TODO: maybe transpose, and group the resources by type instead
@ -96,63 +97,26 @@ in
description = "Operator-facing configuration options for the application"; description = "Operator-facing configuration options for the application";
type = deferredModuleWith { staticModules = [ { _class = "fediversity-application-config"; } ]; }; 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 # TODO(@fricklerhandwerk): this needs a better name
config-mapping = mkOption { config-mapping = mkOption {
description = "Function type for the mapping from application configuration to required resources"; description = "Compute resources required by an application";
type = submodule ( type = submodule functionType;
{ config, ... }: readOnly = true;
{ default = {
options = { input-type = submodule application.config.module;
input-type = mkOption { output-type = application-resources;
type = optionType; implementation = application.config.implementation;
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 = { };
}; };
}; };
}) })
@ -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 { resource-mapping = mkOption {
description = "Function type for the mapping from resources to a (NixOps4) deployment"; description = "Function type for the mapping from resources to a (NixOps4) deployment";
type = submodule ( type = submodule functionType;
{ config, ... }: readOnly = true;
{ default = {
options = { input-type = application-resources;
input-type = mkOption { output-type = nixops4Deployment;
type = optionType; implementation = environment.config.implementation;
default = submodule application-resources; };
# default = types.int; };
}; config-mapping = mkOption {
output-type = mkOption { description = "Mapping from a configuration to a deployment";
type = optionType; type = submodule functionType;
default = nixops4Deployment; readOnly = true;
}; default = {
function-type = mkOption { input-type = config.configuration;
type = optionType; output-type = nixops4Deployment;
readOnly = true; implementation =
default = functionTo config.output-type; cfg:
}; # TODO: check cfg.enable.true
wrapper-type = mkOption { let
type = optionType; required-resources = lib.mapAttrs (
readOnly = true; name: application-settings: config.applications.${name}.resources application-settings
default = functionTo (submodule { ) cfg.applications;
options = { in
input = mkOption { environment.config.resource-mapping.apply required-resources;
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 = { };
}; };
# TODO(@fricklerhandwerk): maybe this should be a separate thing such as `fediversity-setup`, # TODO(@fricklerhandwerk): maybe this should be a separate thing such as `fediversity-setup`,
# which makes explicit which applications and environments are available. # 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. # then the deployments can simply be the result of the function application baked into this module.
deployment = mkOption { deployment = mkOption {
description = "Generate a deployment from a configuration, by applying an environment's resource policies to the applications' resource mappings"; 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; readOnly = true;
default = default = environment.config.config-mapping.apply;
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;
}; };
}; };
}) })

58
deployment/function.nix Normal file
View file

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