From d878e87d38571e0cc710cc6f68f3da0d8b2ada9f Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Tue, 17 Jun 2025 12:12:16 +0200 Subject: [PATCH] draft types --- deployment/application.nix | 85 +++++++++ deployment/default.nix | 293 +++++++++++++---------------- deployment/deployment.nix | 50 +++++ deployment/migration.nix | 28 +++ deployment/options.nix | 2 + deployment/runtime-environment.nix | 194 +++++++++++++++++++ npins/sources.json | 13 -- 7 files changed, 491 insertions(+), 174 deletions(-) create mode 100644 deployment/application.nix create mode 100644 deployment/deployment.nix create mode 100644 deployment/migration.nix create mode 100644 deployment/runtime-environment.nix diff --git a/deployment/application.nix b/deployment/application.nix new file mode 100644 index 00000000..4ecfe630 --- /dev/null +++ b/deployment/application.nix @@ -0,0 +1,85 @@ +{ + lib, + ... +}: +let + inherit (lib) types mkOption; + inherit (types) + attrTag + attrsOf + submodule + submoduleWith + deferredModule + ; +in +{ + options = { + runtime-environments = mkOption { + type = attrsOf (attrTag { + nixos = mkOption { + type = submodule { + options = { + module = mkOption { + description = "The NixOS module of the run-time environment"; + type = deferredModule; + }; + }; + }; + }; + }); + }; + applications = mkOption { + description = "Collection of NixOS modules, each implementing a Fediversity application"; + example.hello = { + enable = true; + module = + { pkgs, ... }: + { + environment.systemPackages = [ pkgs.hello ]; + }; + }; + type = attrsOf (submoduleWith { + description = "A Fediversity application"; + modules = [ + { + options = { + module = mkOption { + description = "The NixOS module to compose into an operator's configuration"; + type = deferredModule; + }; + components = mkOption { + type = attrsOf (attrTag { + file-system-state = mkOption { + desciption = "files stored by the application"; + type = attrsOf (submodule { + options.minSize = types.bytes; + }); + database-state = mkOption { + desciption = "state stored in databases by the application"; + type = attrsOf (submodule { + postgresql = mkOption { + desciption = "state stored in PostgreSQL by the application"; + type = attrsOf (submodule { + options = { + }; + }); + }; + key-val = mkOption { + desciption = "state stored in a key-value store by the application"; + type = attrsOf (submodule { + options = { + }; + }); + }; + }); + }; + }; + }); + }; + }; + } + ]; + }); + }; + }; +} diff --git a/deployment/default.nix b/deployment/default.nix index dbfa4610..d2904a1a 100644 --- a/deployment/default.nix +++ b/deployment/default.nix @@ -37,180 +37,151 @@ panelConfigNullable: let inherit (lib) mkIf; - - ## The convertor from module options to JSON schema does not generate proper - ## JSON schema types, forcing us to use nullable fields for default values. - ## However, working with those fields in the deployment code is annoying (and - ## unusual for Nix programmers), so we sanitize the input here and add back - ## the default value by hand. - nonNull = x: v: if x == null then v else x; - panelConfig = { - domain = nonNull panelConfigNullable.domain "fediversity.net"; - initialUser = nonNull panelConfigNullable.initialUser { - displayName = "Testy McTestface"; - username = "test"; - password = "testtest"; - email = "test@test.com"; - }; - mastodon = nonNull panelConfigNullable.mastodon { enable = false; }; - peertube = nonNull panelConfigNullable.peertube { enable = false; }; - pixelfed = nonNull panelConfigNullable.pixelfed { enable = false; }; - }; in ## Regular arguments of a NixOps4 deployment module. { config, providers, ... }: let - cfg = config.deployment; + cfg = config.deployment.module; in { - options = { - deployment = lib.mkOption { - description = '' - Configuration to be deployed - ''; - # XXX(@fricklerhandwerk): - # misusing this will produce obscure errors that will be truncated by NixOps4 - type = lib.types.submodule ./options.nix; - default = panelConfig; - }; - }; + imports = [ + (import ./deployment.nix { inherit lib panelConfigNullable; }) + ]; - config = { - providers = { inherit (nixops4.modules.nixops4Provider) local; }; + providers = { inherit (nixops4.modules.nixops4Provider) local; }; - resources = - let - ## NOTE: All of these secrets are publicly available in this source file - ## and will end up in the Nix store. We don't care as they are only ever - ## used for testing anyway. - ## - ## FIXME: Generate and store in NixOps4's state. - mastodonS3KeyConfig = - { pkgs, ... }: - { - s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK3515373e4c851ebaad366558"; - s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece597dbb34"; - }; - peertubeS3KeyConfig = - { pkgs, ... }: - { - s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK1f9feea9960f6f95ff404c9b"; - s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7295c4201966a02c2c3d25b5cea4a5ff782966a2415e3a196f91924631191395"; - }; - pixelfedS3KeyConfig = - { pkgs, ... }: - { - s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GKb5615457d44214411e673b7b"; - s3SecretKeyFile = pkgs.writeText "s3SecretKey" "5be6799a88ca9b9d813d1a806b64f15efa49482dbe15339ddfaf7f19cf434987"; - }; - - makeConfigurationResource = resourceModule: config: { - type = providers.local.exec; - imports = [ - nixops4-nixos.modules.nixops4Resource.nixos - resourceModule - - { - ## NOTE: With NixOps4, there are several levels and all of them live - ## in the NixOS module system: - ## - ## 1. Each NixOps4 deployment is a module. - ## 2. Each NixOps4 resource is a module. This very comment is - ## inside an attrset imported as a module in a resource. - ## 3. Each NixOps4 'configuration' resource contains an attribute - ## 'nixos.module', itself a NixOS configuration module. - nixos.module = - { ... }: - { - imports = [ - config - fediversity - ]; - }; - } - ]; + resources = + let + ## NOTE: All of these secrets are publicly available in this source file + ## and will end up in the Nix store. We don't care as they are only ever + ## used for testing anyway. + ## + ## FIXME: Generate and store in NixOps4's state. + mastodonS3KeyConfig = + { pkgs, ... }: + { + s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK3515373e4c851ebaad366558"; + s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece597dbb34"; + }; + peertubeS3KeyConfig = + { pkgs, ... }: + { + s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK1f9feea9960f6f95ff404c9b"; + s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7295c4201966a02c2c3d25b5cea4a5ff782966a2415e3a196f91924631191395"; + }; + pixelfedS3KeyConfig = + { pkgs, ... }: + { + s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GKb5615457d44214411e673b7b"; + s3SecretKeyFile = pkgs.writeText "s3SecretKey" "5be6799a88ca9b9d813d1a806b64f15efa49482dbe15339ddfaf7f19cf434987"; }; - in + makeConfigurationResource = resourceModule: config: { + type = providers.local.exec; + imports = [ + nixops4-nixos.modules.nixops4Resource.nixos + resourceModule - { - garage-configuration = makeConfigurationResource garageConfigurationResource ( - { pkgs, ... }: - mkIf (cfg.mastodon.enable || cfg.peertube.enable || cfg.pixelfed.enable) { - fediversity = { - inherit (cfg) domain; - garage.enable = true; - pixelfed = pixelfedS3KeyConfig { inherit pkgs; }; - mastodon = mastodonS3KeyConfig { inherit pkgs; }; - peertube = peertubeS3KeyConfig { inherit pkgs; }; - }; + { + ## NOTE: With NixOps4, there are several levels and all of them live + ## in the NixOS module system: + ## + ## 1. Each NixOps4 deployment is a module. + ## 2. Each NixOps4 resource is a module. This very comment is + ## inside an attrset imported as a module in a resource. + ## 3. Each NixOps4 'configuration' resource contains an attribute + ## 'nixos.module', itself a NixOS configuration module. + nixos.module = + { ... }: + { + imports = [ + config + fediversity + ]; + }; } - ); - - mastodon-configuration = makeConfigurationResource mastodonConfigurationResource ( - { pkgs, ... }: - mkIf cfg.mastodon.enable { - fediversity = { - inherit (cfg) domain; - temp.initialUser = { - inherit (cfg.initialUser) username email displayName; - # FIXME: disgusting, but nvm, this is going to be replaced by - # proper central authentication at some point - passwordFile = pkgs.writeText "password" cfg.initialUser.password; - }; - - mastodon = mastodonS3KeyConfig { inherit pkgs; } // { - enable = true; - }; - - temp.cores = 1; # FIXME: should come from NixOps4 eventually - }; - } - ); - - peertube-configuration = makeConfigurationResource peertubeConfigurationResource ( - { pkgs, ... }: - mkIf cfg.peertube.enable { - fediversity = { - inherit (cfg) domain; - temp.initialUser = { - inherit (cfg.initialUser) username email displayName; - # FIXME: disgusting, but nvm, this is going to be replaced by - # proper central authentication at some point - passwordFile = pkgs.writeText "password" cfg.initialUser.password; - }; - - peertube = peertubeS3KeyConfig { inherit pkgs; } // { - enable = true; - ## NOTE: Only ever used for testing anyway. - ## - ## FIXME: Generate and store in NixOps4's state. - secretsFile = pkgs.writeText "secret" "574e093907d1157ac0f8e760a6deb1035402003af5763135bae9cbd6abe32b24"; - }; - }; - } - ); - - pixelfed-configuration = makeConfigurationResource pixelfedConfigurationResource ( - { pkgs, ... }: - mkIf cfg.pixelfed.enable { - fediversity = { - inherit (cfg) domain; - temp.initialUser = { - inherit (cfg.initialUser) username email displayName; - # FIXME: disgusting, but nvm, this is going to be replaced by - # proper central authentication at some point - passwordFile = pkgs.writeText "password" cfg.initialUser.password; - }; - - pixelfed = pixelfedS3KeyConfig { inherit pkgs; } // { - enable = true; - }; - }; - } - ); + ]; }; - }; + + in + + { + garage-configuration = makeConfigurationResource garageConfigurationResource ( + { pkgs, ... }: + mkIf (cfg.mastodon.enable || cfg.peertube.enable || cfg.pixelfed.enable) { + fediversity = { + inherit (cfg) domain; + garage.enable = true; + pixelfed = pixelfedS3KeyConfig { inherit pkgs; }; + mastodon = mastodonS3KeyConfig { inherit pkgs; }; + peertube = peertubeS3KeyConfig { inherit pkgs; }; + }; + } + ); + + mastodon-configuration = makeConfigurationResource mastodonConfigurationResource ( + { pkgs, ... }: + mkIf cfg.mastodon.enable { + fediversity = { + inherit (cfg) domain; + temp.initialUser = { + inherit (cfg.initialUser) username email displayName; + # FIXME: disgusting, but nvm, this is going to be replaced by + # proper central authentication at some point + passwordFile = pkgs.writeText "password" cfg.initialUser.password; + }; + + mastodon = mastodonS3KeyConfig { inherit pkgs; } // { + enable = true; + }; + + temp.cores = 1; # FIXME: should come from NixOps4 eventually + }; + } + ); + + peertube-configuration = makeConfigurationResource peertubeConfigurationResource ( + { pkgs, ... }: + mkIf cfg.peertube.enable { + fediversity = { + inherit (cfg) domain; + temp.initialUser = { + inherit (cfg.initialUser) username email displayName; + # FIXME: disgusting, but nvm, this is going to be replaced by + # proper central authentication at some point + passwordFile = pkgs.writeText "password" cfg.initialUser.password; + }; + + peertube = peertubeS3KeyConfig { inherit pkgs; } // { + enable = true; + ## NOTE: Only ever used for testing anyway. + ## + ## FIXME: Generate and store in NixOps4's state. + secretsFile = pkgs.writeText "secret" "574e093907d1157ac0f8e760a6deb1035402003af5763135bae9cbd6abe32b24"; + }; + }; + } + ); + + pixelfed-configuration = makeConfigurationResource pixelfedConfigurationResource ( + { pkgs, ... }: + mkIf cfg.pixelfed.enable { + fediversity = { + inherit (cfg) domain; + temp.initialUser = { + inherit (cfg.initialUser) username email displayName; + # FIXME: disgusting, but nvm, this is going to be replaced by + # proper central authentication at some point + passwordFile = pkgs.writeText "password" cfg.initialUser.password; + }; + + pixelfed = pixelfedS3KeyConfig { inherit pkgs; } // { + enable = true; + }; + }; + } + ); + }; } diff --git a/deployment/deployment.nix b/deployment/deployment.nix new file mode 100644 index 00000000..24245a38 --- /dev/null +++ b/deployment/deployment.nix @@ -0,0 +1,50 @@ +{ + lib, + panelConfigNullable, + ... +}: +let + inherit (lib) types mkOption; + + ## The convertor from module options to JSON schema does not generate proper + ## JSON schema types, forcing us to use nullable fields for default values. + ## However, working with those fields in the deployment code is annoying (and + ## unusual for Nix programmers), so we sanitize the input here and add back + ## the default value by hand. + nonNull = x: v: if x == null then v else x; + panelConfig = { + domain = nonNull panelConfigNullable.domain "fediversity.net"; + initialUser = nonNull panelConfigNullable.initialUser { + displayName = "Testy McTestface"; + username = "test"; + password = "testtest"; + email = "test@test.com"; + }; + mastodon = nonNull panelConfigNullable.mastodon { enable = false; }; + peertube = nonNull panelConfigNullable.peertube { enable = false; }; + pixelfed = nonNull panelConfigNullable.pixelfed { enable = false; }; + }; +in +{ + options = { + deployment = types.submodule { + module = mkOption { + description = '' + Configuration to be deployed + ''; + # XXX(@fricklerhandwerk): + # misusing this will produce obscure errors that will be truncated by NixOps4 + type = lib.types.submodule ./options.nix; + default = panelConfig; + }; + state = mkOption { + description = '' + State of the deployment + ''; + # TODO: TF state + type = types.deferredModule; + default = { }; + }; + }; + }; +} diff --git a/deployment/migration.nix b/deployment/migration.nix new file mode 100644 index 00000000..18b9ee3d --- /dev/null +++ b/deployment/migration.nix @@ -0,0 +1,28 @@ +{ + lib, + ... +}: +let + inherit (lib) types mkOption; +in +{ + options = { + migration = mkOption { + description = "Migration of a Fediversity deployment to a Fediversity run-time environment"; + type = + with types; + submodule { + runtime-environment = mkOption { + description = "Run-time environment to migrate the deployment to"; + type = lib.types.submodule ./options.nix; + # default = { }; + }; + deployment = mkOption { + description = "Deployment to migrate"; + type = lib.types.submodule ./deployment.nix; + # default = { }; + }; + }; + }; + }; +} diff --git a/deployment/options.nix b/deployment/options.nix index 0c5fa078..f5904e42 100644 --- a/deployment/options.nix +++ b/deployment/options.nix @@ -8,6 +8,8 @@ This can be fixed if we made the converter aware of [`$defs`], but that would likely amount to half a rewrite. [`$defs`]: https://json-schema.org/understanding-json-schema/structuring#defs + + An example deployment configuration may be found at `configuration.sample.json`. */ { lib, diff --git a/deployment/runtime-environment.nix b/deployment/runtime-environment.nix new file mode 100644 index 00000000..df332b48 --- /dev/null +++ b/deployment/runtime-environment.nix @@ -0,0 +1,194 @@ +{ + lib, + ... +}: +let + inherit (lib) types mkOption; + ssh = + with types; + (submodule { + host = mkOption { + description = "the host to access by SSH"; + type = str; + }; + username = mkOption { + description = "the SSH user to use"; + type = nullOr str; + default = null; + }; + authentication = mkOption { + desciption = "authentication method"; + type = attrsOf (attrTag { + private-key = mkOption { + description = "path to the user's SSH private key"; + type = str; + example = "/root/.ssh/id_ed25519"; + }; + password = mkOption { + description = "SSH password"; + # TODO: mark as sensitive + type = str; + }; + }); + }; + }); +in +{ + options = { + infrastructure = mkOption { + description = '' + Infrastructure for Fediversity applications to run on. + + For adding new types, see [`nixos-generators`](https://github.com/nix-community/nixos-generators#supported-formats). + ''; + type = + with types; + attrsOf (attrTag { + single-ssh-host = mkOption { + description = "A single host to deploy to by SSH."; + type = submodule (self: { + deploy = mkOption { + description = "deployment script"; + type = str; + readOnly = true; + default = ''''; + }; + module = mkOption { + description = "NixOS module"; + type = deferredModule; + default = { + services.openssh.enable = true; + # users.users.root.openssh.authorizedKeys.keys = [ + # "" + # ]; + }; + readOnly = true; + }; + ssh = mkOption { + description = "SSH connection info"; + type = ssh; + }; + }); + }; + vm = mkOption { + description = "A VM to deploy to."; + type = submodule (self: { + deploy = mkOption { + description = "deployment script"; + type = str; + readOnly = true; + default = ''''; + }; + module = mkOption { + description = "NixOS module"; + type = deferredModule; + default = { }; + readOnly = true; + }; + }); + }; + single-nixos-machine-via-usb = mkOption { + description = "A machine to install the deployment to by live USB."; + type = submodule (self: { + deploy = mkOption { + description = "deployment script"; + type = str; + readOnly = true; + default = ''''; + }; + # TODO: maybe steal some data structures from NixOS + module = mkOption { + description = "NixOS module"; + type = deferredModule; + default = { }; + readOnly = true; + }; + hasNetwork = mkOption { + type = types.bool; + }; + disks = mkOption { + type = + with types; + attrsOf (submodule { + options.size = mkOption { + type = types.bytes; + }; + }); + }; + }); + }; + proxmox = mkOption { + description = '' + A ProxmoX-VE instance to deploy to. + See: https://registry.terraform.io/providers/bpg/proxmox/latest/docs + ''; + type = submodule (self: { + deploy = mkOption { + description = "deployment script"; + type = str; + readOnly = true; + default = ''''; + }; + module = mkOption { + description = "NixOS module"; + type = deferredModule; + default = { }; + readOnly = true; + }; + endpoint = mkOption { + description = "API endpoint URL"; + type = str; + default = "https://localhost:8006/"; + }; + authentication = mkOption { + description = '' + ProxmoX authentication method. + See: https://registry.terraform.io/providers/bpg/proxmox/latest/docs#authentication-methods-comparison + ''; + type = attrsOf (attrTag { + api-token = mkOption { + description = "API token"; + # TODO: mark as sensitive + type = str; + }; + ticket = submodule { + auth-ticket = mkOption { + description = "Auth ticket"; + # TODO: mark as sensitive + type = str; + }; + csrf-token = mkOption { + description = "CSRF prevention token"; + # TODO: mark as sensitive + type = str; + }; + }; + user = submodule { + username = mkOption { + description = "Username with realm"; + type = str; + example = "root@pam"; + }; + password = mkOption { + description = "User password"; + # TODO: mark as sensitive + type = str; + }; + }; + }); + }; + insecure = mkOption { + description = "Skip TLS verification"; + type = bool; + default = false; + }; + ssh = mkOption { + description = "Info to access a remote ProxmoX by SSH."; + type = ssh; + }; + }); + }; + }); + }; + }; +} diff --git a/npins/sources.json b/npins/sources.json index a96ffcb2..e1bb3d61 100644 --- a/npins/sources.json +++ b/npins/sources.json @@ -112,19 +112,6 @@ "url": "https://api.github.com/repos/bigskysoftware/htmx/tarball/v2.0.4", "hash": "1c4zm3b7ym01ijydiss4amd14mv5fbgp1n71vqjk4alc35jlnqy2" }, - "nix-unit": { - "type": "Git", - "repository": { - "type": "GitHub", - "owner": "nix-community", - "repo": "nix-unit" - }, - "branch": "main", - "submodules": false, - "revision": "e9d81f6cffe67681e7c04a967d29f18c2c540af5", - "url": "https://github.com/nix-community/nix-unit/archive/e9d81f6cffe67681e7c04a967d29f18c2c540af5.tar.gz", - "hash": "1wms0wxwvxac1r1daihj5wsx1nghfk5hwdvy5cpgq481bp9x4cjn" - }, "nixpkgs": { "type": "Git", "repository": {