From da16381a9bc3c8160d73b33c26663498bb4c09dd Mon Sep 17 00:00:00 2001 From: Kiara Grouwstra Date: Sun, 23 Nov 2025 16:08:51 +0100 Subject: [PATCH] panel deploys thru model Signed-off-by: Kiara Grouwstra --- deployment/fediversity/ssh-hosts.nix | 2 +- deployment/options.nix | 126 +++++++++--------- deployment/utils-test.nix | 6 + deployment/utils.nix | 7 + flake.nix | 6 + panel/default.nix | 8 +- panel/nix/configuration.nix | 18 +-- panel/nix/package.nix | 5 +- .../panel/tests/test_configuration_form.py | 2 +- panel/src/panel/views.py | 9 +- 10 files changed, 103 insertions(+), 86 deletions(-) diff --git a/deployment/fediversity/ssh-hosts.nix b/deployment/fediversity/ssh-hosts.nix index 83109941..6dc682e0 100644 --- a/deployment/fediversity/ssh-hosts.nix +++ b/deployment/fediversity/ssh-hosts.nix @@ -4,6 +4,7 @@ ancilliary, sources ? import ../../npins, conf ? { }, + key-file ? null, ... }@args: let @@ -19,7 +20,6 @@ let }; sshOpts = [ ]; username = "root"; - key-file = null; apps = lib.attrNames host-mapping; nodes = lib.attrNames ancilliary ++ apps; hosts = ancilliary // host-mapping; diff --git a/deployment/options.nix b/deployment/options.nix index c0a5e8d7..ba47b3cb 100644 --- a/deployment/options.nix +++ b/deployment/options.nix @@ -14,61 +14,69 @@ ... }: let - inherit (lib) types mkOption; + inherit (lib) types mkOption mkEnableOption; + inherit (types) + enum + nullOr + submodule + str + ; in { _class = "nixops4Deployment"; options = { - enable = lib.mkEnableOption "Fediversity configuration"; + enable = mkEnableOption "Fediversity configuration"; domain = mkOption { - type = - with types; - enum [ - "fediversity.net" - ]; + type = enum [ + "fediversity.net" + ]; description = '' Apex domain under which the services will be deployed. ''; default = "fediversity.net"; }; - pixelfed = mkOption { + applications = mkOption { description = '' - Configuration for the Pixelfed service + Applications to deploy. ''; - type = - with types; - nullOr (submodule { - options = { - enable = lib.mkEnableOption "Pixelfed"; + type = nullOr (submodule { + options = { + pixelfed = mkOption { + description = '' + Configuration for the Pixelfed service + ''; + type = nullOr (submodule { + options = { + enable = mkEnableOption "Pixelfed"; + }; + }); + default = null; }; - }); - default = null; - }; - peertube = mkOption { - description = '' - Configuration for the PeerTube service - ''; - type = - with types; - nullOr (submodule { - options = { - enable = lib.mkEnableOption "Peertube"; + peertube = mkOption { + description = '' + Configuration for the PeerTube service + ''; + type = nullOr (submodule { + options = { + enable = mkEnableOption "Peertube"; + }; + }); + default = null; }; - }); - default = null; - }; - mastodon = mkOption { - description = '' - Configuration for the Mastodon service - ''; - type = - with types; - nullOr (submodule { - options = { - enable = lib.mkEnableOption "Mastodon"; + mastodon = mkOption { + description = '' + Configuration for the Mastodon service + ''; + type = nullOr (submodule { + options = { + enable = mkEnableOption "Mastodon"; + }; + }); + default = null; }; - }); + }; + }); default = null; }; initialUser = mkOption { @@ -76,28 +84,26 @@ in Some services require an initial user to access them. This option sets the credentials for such an initial user. ''; - type = - with types; - nullOr (submodule { - options = { - displayName = mkOption { - type = types.str; - description = "Display name of the user"; - }; - username = mkOption { - type = types.str; - description = "Username for login"; - }; - email = mkOption { - type = types.str; - description = "User's email address"; - }; - password = mkOption { - type = types.str; - description = "Password for login"; - }; + type = nullOr (submodule { + options = { + displayName = mkOption { + type = str; + description = "Display name of the user"; }; - }); + username = mkOption { + type = str; + description = "Username for login"; + }; + email = mkOption { + type = str; + description = "User's email address"; + }; + password = mkOption { + type = str; + description = "Password for login"; + }; + }; + }); default = null; }; }; diff --git a/deployment/utils-test.nix b/deployment/utils-test.nix index 3b8434db..a2707ee6 100644 --- a/deployment/utils-test.nix +++ b/deployment/utils-test.nix @@ -1,6 +1,7 @@ let inherit (import ../default.nix { }) pkgs; inherit (pkgs.callPackage ./utils.nix { }) + optionalEnv mapKeys getSomeAttrs evalOption @@ -16,6 +17,11 @@ in { _class = "nix-unit"; + test-optionalEnv = { + expr = optionalEnv "NOT_SET"; + expected = null; + }; + test-mapKeys = { expr = mapKeys (k: "${k}${k}") { a = 1; }; expected = { diff --git a/deployment/utils.nix b/deployment/utils.nix index 0e101901..6589e369 100644 --- a/deployment/utils.nix +++ b/deployment/utils.nix @@ -5,6 +5,13 @@ ... }: rec { + optionalEnv = + k: + let + v = builtins.getEnv k; + in + if v != "" then v else null; + mapKeys = keyMapper: lib.mapAttrs' ( diff --git a/flake.nix b/flake.nix index d46990b3..7585ed9f 100644 --- a/flake.nix +++ b/flake.nix @@ -54,6 +54,7 @@ }; apps = let + inherit (pkgs.callPackage ./deployment/utils.nix { }) optionalEnv; default-configuration = builtins.fromJSON ( let env = builtins.getEnv "DEPLOYMENT"; @@ -74,6 +75,11 @@ (import ./deployment/fediversity { inherit system host-mapping; ancilliary.garage = "test01"; + key-file = + let + key = optionalEnv "SSH_PRIVATE_KEY_FILE"; + in + builtins.trace ("SSH_PRIVATE_KEY_FILE: " + builtins.toString key) key; conf."default-configuration" = default-configuration // { enable = true; applications = lib.mapAttrs (_app: _: { enable = true; }) host-mapping; diff --git a/panel/default.nix b/panel/default.nix index a8f716c9..2e053d94 100644 --- a/panel/default.nix +++ b/panel/default.nix @@ -9,6 +9,7 @@ }: let inherit (pkgs) lib; + inherit (pkgs.callPackage ../deployment/utils.nix { }) optionalEnv; manage = pkgs.writeScriptBin "manage" '' exec ${pkgs.lib.getExe pkgs.python3} ${toString ./src/manage.py} $@ ''; @@ -28,10 +29,15 @@ in ]; env = { DEPLOYMENT_FLAKE = toString ../.; - DEPLOYMENT_NAME = "test"; + DEPLOYMENT_NAME = "operator"; NPINS_DIRECTORY = toString ../npins; CREDENTIALS_DIRECTORY = toString ./.credentials; DATABASE_URL = "sqlite:///${toString ./src}/db.sqlite3"; + SSH_PRIVATE_KEY_FILE = + let + key = optionalEnv "SSH_PRIVATE_KEY_FILE"; + in + builtins.trace ("SSH_PRIVATE_KEY_FILE: " + builtins.toString key) key; }; shellHook = '' ${lib.concatStringsSep "\n" ( diff --git a/panel/nix/configuration.nix b/panel/nix/configuration.nix index 4df994c1..79ba2d56 100644 --- a/panel/nix/configuration.nix +++ b/panel/nix/configuration.nix @@ -137,19 +137,6 @@ in type = types.attrsOf types.path; default = { }; }; - nixops4Package = mkOption { - type = types.package; - description = '' - A package providing NixOps4. - - TODO: This should not be at the level of the NixOS module, but instead - at the level of the panel's package. Until one finds a way to grab - NixOps4 from the package's npins-based code, we will have to do with - this workaround. - ''; - default = pkgs.nixops4; - }; - deployment = { flake = mkOption { type = types.path; @@ -160,7 +147,7 @@ in }; name = mkOption { type = types.str; - default = "test"; + default = "operator"; description = '' The name of the deployment within the flake. ''; @@ -213,9 +200,6 @@ in path = [ python-environment manage-service - - ## NixOps4 and its dependencies - cfg.nixops4Package pkgs.nix pkgs.openssh pkgs.git diff --git a/panel/nix/package.nix b/panel/nix/package.nix index 76d726d0..84a5fabe 100644 --- a/panel/nix/package.nix +++ b/panel/nix/package.nix @@ -8,6 +8,7 @@ sources ? import ../../npins, }: let + inherit (builtins) toFile toJSON; src = with lib.fileset; toSource { @@ -33,7 +34,7 @@ let let jsonschema = callPackage "${sources.clan-core}/lib/jsonschema" { } { }; frontend-options = jsonschema.parseModule ../../deployment/options.nix; - schema = with builtins; toFile "schema.json" (toJSON frontend-options); + schema = toFile "schema.json" (toJSON frontend-options); in [ { @@ -69,7 +70,7 @@ python3.pkgs.buildPythonPackage { preBuild = '' echo "recursive-include ${name} *" > MANIFEST.in - cp ${builtins.toFile "source" pyproject-toml} pyproject.toml + cp ${toFile "source" pyproject-toml} pyproject.toml ''; propagatedBuildInputs = diff --git a/panel/src/panel/tests/test_configuration_form.py b/panel/src/panel/tests/test_configuration_form.py index 14a0df64..78a02632 100644 --- a/panel/src/panel/tests/test_configuration_form.py +++ b/panel/src/panel/tests/test_configuration_form.py @@ -32,7 +32,7 @@ class ConfigurationForm(TestCase): form_data = context['serializer'].instance.dict().copy() form_data = { "enable": True, - "mastodon.enable": True, + "applications.mastodon.enable": True, } response = self.client.post(self.config_url, data=form_data, follow=True) diff --git a/panel/src/panel/views.py b/panel/src/panel/views.py index 2f603002..028950e2 100644 --- a/panel/src/panel/views.py +++ b/panel/src/panel/views.py @@ -90,15 +90,16 @@ class DeploymentStatus(ConfigurationForm): def deployment(self, config: BaseModel): env = { "PATH": os.environ.get("PATH"), + "SSH_PRIVATE_KEY_FILE": os.environ.get("SSH_PRIVATE_KEY_FILE"), # pass in form info to our deployment "DEPLOYMENT": config.json() } cmd = [ - "nixops4", - "apply", - settings.deployment_name, + "nix", + "run", + "--impure", + f".#{settings.deployment_name}", "--show-trace", - "--no-interactive", ] deployment_result = subprocess.run( cmd,