Fediversity/deployment/data-model-test.nix
Kiara Grouwstra efbe7deddd
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!
2025-08-17 23:17:55 +02:00

204 lines
7.7 KiB
Nix

let
inherit (import ../default.nix { }) pkgs inputs;
inherit (pkgs) lib;
inherit (lib) mkOption types;
eval =
module:
(lib.evalModules {
specialArgs = {
inherit inputs;
};
modules = [
module
./data-model.nix
];
}).config;
nixops4Deployment = inputs.nixops4.modules.nixops4Deployment.default;
inherit (inputs.nixops4.lib) mkDeployment;
in
{
_class = "nix-unit";
test-eval = {
/**
This tests a very simple arrangement that features all ingredients of the Fediversity business logic:
application, resource, environment, deployment; and wires it all up in one end-to-end exercise.
- The dummy resource is a login shell made available for some user.
- The dummy application is `hello` that requires a shell to be deployed.
- The dummy environment is a single NixOS VM that hosts one login shell, for the operator.
- The dummy configuration enables the `hello` application.
This will produce a NixOps4 deployment for a NixOS VM with a login shell for the operator and `hello` available.
*/
expr =
let
fediversity = eval (
{ config, ... }:
{
config = {
resources.login-shell = {
description = "The operator needs to be able to log into the shell";
request =
{ ... }:
{
_class = "fediversity-resource-request";
options = {
wheel = mkOption {
description = "Whether the login user needs root permissions";
type = types.bool;
default = false;
};
packages = mkOption {
description = "Packages that need to be available in the user environment";
type = with types; attrsOf package;
};
};
};
policy =
{ config, ... }:
{
_class = "fediversity-resource-policy";
options = {
username = mkOption {
description = "Username for the operator";
type = types.str; # TODO: use the proper constraints from NixOS
};
wheel = mkOption {
description = "Whether to allow login with root permissions";
type = types.bool;
default = false;
};
};
config = {
resource-type = types.raw; # TODO: splice out the user type from NixOS
apply =
requests:
let
# Filter out requests that need wheel if policy doesn't allow it
validRequests = lib.filterAttrs (
_name: req: !req.login-shell.wheel || config.wheel
) requests.resources;
in
lib.optionalAttrs (validRequests != { }) {
${config.username} = {
isNormalUser = true;
packages =
with lib;
attrValues (concatMapAttrs (_name: request: request.login-shell.packages) validRequests);
extraGroups = lib.optional config.wheel "wheel";
};
};
};
};
};
applications.hello =
{ ... }:
{
description = ''Command-line tool that will print "Hello, world!" on the terminal'';
module =
{ ... }:
{
options.enable = lib.mkEnableOption "Hello in the shell";
};
implementation =
cfg:
lib.optionalAttrs cfg.enable {
resources.hello.login-shell.packages.hello = pkgs.hello;
};
};
environments.single-nixos-vm =
{ config, ... }:
{
resources.operator-environment.login-shell.username = "operator";
implementation =
requests:
{ providers, ... }:
{
providers = {
inherit (inputs.nixops4.modules.nixops4Provider) local;
};
resources.the-machine = {
type = providers.local.exec;
imports = [
inputs.nixops4-nixos.modules.nixops4Resource.nixos
];
nixos.module =
{ ... }:
{
users.users = config.resources.shell.login-shell.apply (
lib.filterAttrs (_name: value: value ? login-shell) requests
);
};
};
};
};
};
options = {
example-configuration = mkOption {
type = config.configuration;
readOnly = true;
default = {
enable = true;
applications.hello.enable = true;
};
};
example-deployment = mkOption {
type = types.submodule nixops4Deployment;
readOnly = true;
default = config.environments.single-nixos-vm.deployment config.example-configuration;
};
};
}
);
resources = fediversity.applications.hello.resources fediversity.example-configuration.applications.hello;
hello-shell = resources.resources.hello.login-shell;
environment = fediversity.environments.single-nixos-vm.resources.operator-environment.login-shell;
result = mkDeployment {
modules = [
(fediversity.environments.single-nixos-vm.deployment fediversity.example-configuration)
];
};
in
{
number-of-resources = with lib; length (attrNames fediversity.resources);
inherit (fediversity) example-configuration;
hello-package-exists = hello-shell.packages ? hello;
wheel-required = hello-shell.wheel;
wheel-allowed = environment.wheel;
operator-shell =
let
operator = (environment.apply resources).operator;
in
{
inherit (operator) isNormalUser;
packages = map (p: "${p.pname}") operator.packages;
extraGroups = operator.extraGroups;
};
deployment = {
inherit (result) _type;
deploymentFunction = lib.isFunction result.deploymentFunction;
getProviders = lib.isFunction result.getProviders;
};
};
expected = {
number-of-resources = 1;
example-configuration = {
enable = true;
applications.hello.enable = true;
};
hello-package-exists = true;
wheel-required = false;
wheel-allowed = false;
operator-shell = {
isNormalUser = true;
packages = [ "hello" ];
extraGroups = [ ];
};
deployment = {
_type = "nixops4Deployment";
deploymentFunction = true;
getProviders = true;
};
};
};
}