From 53458600066e404c2088f50b2a3287301c7842e8 Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 6 Jul 2025 21:42:25 +0200 Subject: [PATCH] data model: add run-time configuration (#437) based on @fricklerhandwerk's work at https://git.fediversity.eu/fricklerhandwerk/Fediversity/compare/main...data-model-as-diagram part of #103. supersedes #402. on the application model, feedback on a [sample implementation](https://git.fediversity.eu/kiara/Fediversity/pulls/2) welcome as well. Reviewed-on: https://git.fediversity.eu/Fediversity/Fediversity/pulls/437 Reviewed-by: Valentin Gagarin Co-authored-by: Kiara Grouwstra Co-committed-by: Kiara Grouwstra --- default.nix | 3 + deployment/data-model-test.nix | 67 ++++++++++++++-------- deployment/data-model.nix | 100 ++++++++++++++++++++++++--------- deployment/function.nix | 37 ++++++++++++ 4 files changed, 157 insertions(+), 50 deletions(-) create mode 100644 deployment/function.nix diff --git a/default.nix b/default.nix index 70c5aaf5..41b9d1fc 100644 --- a/default.nix +++ b/default.nix @@ -9,6 +9,8 @@ let git-hooks gitignore ; + inherit (import sources.flake-inputs) import-flake; + inputs = (import-flake { src = ./.; }).inputs; inherit (pkgs) lib; inherit (import sources.flake-inputs) import-flake; inherit ((import-flake { src = ./.; }).inputs) nixops4; @@ -76,6 +78,7 @@ in # re-export inputs so they can be overridden granularly # (they can't be accessed from the outside any other way) inherit + inputs sources system pkgs diff --git a/deployment/data-model-test.nix b/deployment/data-model-test.nix index a988122d..ac35df39 100644 --- a/deployment/data-model-test.nix +++ b/deployment/data-model-test.nix @@ -1,9 +1,13 @@ let - inherit (import ../default.nix { }) pkgs; + inherit (import ../default.nix { }) pkgs inputs; inherit (pkgs) lib; + inherit (lib) mkOption; eval = module: (lib.evalModules { + specialArgs = { + inherit inputs; + }; modules = [ module ./data-model.nix @@ -16,32 +20,51 @@ in test-eval = { expr = let - example = eval { - runtime-environments.bar.nixos = { - module = - { ... }: - { - system.stateVersion = "25.05"; + fediversity = eval ( + { config, ... }: + { + config = { + 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 { + dummy.login-shell.packages.hello = pkgs.hello; + }; + }; + }; + options = { + example-configuration = mkOption { + type = config.configuration; + readOnly = true; + default = { + enable = true; + applications.hello.enable = true; + }; }; - }; - applications.foo = { - module = - { pkgs, ... }: - { - environment.systemPackages = [ - pkgs.hello - ]; - }; - }; - }; + }; + } + ); in { - has-runtime = lib.isAttrs example.runtime-environments.bar.nixos.module; - has-application = lib.isAttrs example.applications.foo.module; + inherit (fediversity) + example-configuration + ; }; expected = { - has-runtime = true; - has-application = true; + example-configuration = { + enable = true; + applications.hello.enable = true; + }; }; }; } diff --git a/deployment/data-model.nix b/deployment/data-model.nix index fd89cb4f..8f584af4 100644 --- a/deployment/data-model.nix +++ b/deployment/data-model.nix @@ -1,45 +1,89 @@ { lib, + config, ... }: let - inherit (lib) types mkOption; + inherit (lib) mkOption types; + inherit (lib.types) + attrsOf + attrTag + deferredModuleWith + submodule + optionType + functionTo + ; + + functionType = import ./function.nix; + application-resources = { + options.resources = mkOption { + # TODO: maybe transpose, and group the resources by type instead + type = attrsOf ( + attrTag (lib.mapAttrs (_name: resource: mkOption { type = resource.request; }) config.resources) + ); + }; + }; in -with types; { _class = "nixops4Deployment"; options = { - runtime-environments = mkOption { - description = "Collection of runtime environments into which applications can be deployed"; - type = attrsOf (attrTag { - nixos = mkOption { - description = "A single NixOS machine"; - type = submodule { - options = { - module = mkOption { - description = "The NixOS module describing the base configuration for that machine"; - type = deferredModule; + 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; + }; + config-mapping = mkOption { + description = "Function type for the mapping from application configuration to required resources"; + type = submodule functionType; + readOnly = true; + default = { + input-type = application.config.module; + output-type = application-resources; }; }; }; - }; - }); + }) + ); }; - applications = mkOption { - description = "Collection of Fediversity applications"; - type = attrsOf (submoduleWith { - modules = [ - { - options = { - module = mkOption { - description = "The NixOS module for that application, for configuring that application"; - type = deferredModule; - }; - }; - } - ]; - }); + 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; + }; + }; }; }; } diff --git a/deployment/function.nix b/deployment/function.nix new file mode 100644 index 00000000..d1a047f0 --- /dev/null +++ b/deployment/function.nix @@ -0,0 +1,37 @@ +/** + Modular function type +*/ +{ config, lib, ... }: +let + inherit (lib) mkOption types; + inherit (types) + deferredModule + submodule + functionTo + optionType + ; +in +{ + options = { + input-type = mkOption { + type = deferredModule; + }; + output-type = mkOption { + type = deferredModule; + }; + function-type = mkOption { + type = optionType; + readOnly = true; + default = functionTo (submodule { + options = { + input = mkOption { + type = submodule config.input-type; + }; + output = mkOption { + type = submodule config.output-type; + }; + }; + }); + }; + }; +}