panel deploys thru model

Signed-off-by: Kiara Grouwstra <kiara@procolix.eu>
This commit is contained in:
Kiara Grouwstra 2025-11-23 16:08:51 +01:00
parent 1fef30fa58
commit da16381a9b
Signed by: kiara
SSH key fingerprint: SHA256:COspvLoLJ5WC5rFb9ZDe5urVCkK4LJZOsjfF4duRJFU
10 changed files with 103 additions and 86 deletions

View file

@ -4,6 +4,7 @@
ancilliary, ancilliary,
sources ? import ../../npins, sources ? import ../../npins,
conf ? { }, conf ? { },
key-file ? null,
... ...
}@args: }@args:
let let
@ -19,7 +20,6 @@ let
}; };
sshOpts = [ ]; sshOpts = [ ];
username = "root"; username = "root";
key-file = null;
apps = lib.attrNames host-mapping; apps = lib.attrNames host-mapping;
nodes = lib.attrNames ancilliary ++ apps; nodes = lib.attrNames ancilliary ++ apps;
hosts = ancilliary // host-mapping; hosts = ancilliary // host-mapping;

View file

@ -14,61 +14,69 @@
... ...
}: }:
let let
inherit (lib) types mkOption; inherit (lib) types mkOption mkEnableOption;
inherit (types)
enum
nullOr
submodule
str
;
in in
{ {
_class = "nixops4Deployment"; _class = "nixops4Deployment";
options = { options = {
enable = lib.mkEnableOption "Fediversity configuration"; enable = mkEnableOption "Fediversity configuration";
domain = mkOption { domain = mkOption {
type = type = enum [
with types; "fediversity.net"
enum [ ];
"fediversity.net"
];
description = '' description = ''
Apex domain under which the services will be deployed. Apex domain under which the services will be deployed.
''; '';
default = "fediversity.net"; default = "fediversity.net";
}; };
pixelfed = mkOption { applications = mkOption {
description = '' description = ''
Configuration for the Pixelfed service Applications to deploy.
''; '';
type = type = nullOr (submodule {
with types; options = {
nullOr (submodule { pixelfed = mkOption {
options = { description = ''
enable = lib.mkEnableOption "Pixelfed"; Configuration for the Pixelfed service
'';
type = nullOr (submodule {
options = {
enable = mkEnableOption "Pixelfed";
};
});
default = null;
}; };
}); peertube = mkOption {
default = null; description = ''
}; Configuration for the PeerTube service
peertube = mkOption { '';
description = '' type = nullOr (submodule {
Configuration for the PeerTube service options = {
''; enable = mkEnableOption "Peertube";
type = };
with types; });
nullOr (submodule { default = null;
options = {
enable = lib.mkEnableOption "Peertube";
}; };
}); mastodon = mkOption {
default = null; description = ''
}; Configuration for the Mastodon service
mastodon = mkOption { '';
description = '' type = nullOr (submodule {
Configuration for the Mastodon service options = {
''; enable = mkEnableOption "Mastodon";
type = };
with types; });
nullOr (submodule { default = null;
options = {
enable = lib.mkEnableOption "Mastodon";
}; };
}); };
});
default = null; default = null;
}; };
initialUser = mkOption { initialUser = mkOption {
@ -76,28 +84,26 @@ in
Some services require an initial user to access them. Some services require an initial user to access them.
This option sets the credentials for such an initial user. This option sets the credentials for such an initial user.
''; '';
type = type = nullOr (submodule {
with types; options = {
nullOr (submodule { displayName = mkOption {
options = { type = str;
displayName = mkOption { description = "Display name of the user";
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";
};
}; };
}); 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; default = null;
}; };
}; };

View file

@ -1,6 +1,7 @@
let let
inherit (import ../default.nix { }) pkgs; inherit (import ../default.nix { }) pkgs;
inherit (pkgs.callPackage ./utils.nix { }) inherit (pkgs.callPackage ./utils.nix { })
optionalEnv
mapKeys mapKeys
getSomeAttrs getSomeAttrs
evalOption evalOption
@ -16,6 +17,11 @@ in
{ {
_class = "nix-unit"; _class = "nix-unit";
test-optionalEnv = {
expr = optionalEnv "NOT_SET";
expected = null;
};
test-mapKeys = { test-mapKeys = {
expr = mapKeys (k: "${k}${k}") { a = 1; }; expr = mapKeys (k: "${k}${k}") { a = 1; };
expected = { expected = {

View file

@ -5,6 +5,13 @@
... ...
}: }:
rec { rec {
optionalEnv =
k:
let
v = builtins.getEnv k;
in
if v != "" then v else null;
mapKeys = mapKeys =
keyMapper: keyMapper:
lib.mapAttrs' ( lib.mapAttrs' (

View file

@ -54,6 +54,7 @@
}; };
apps = apps =
let let
inherit (pkgs.callPackage ./deployment/utils.nix { }) optionalEnv;
default-configuration = builtins.fromJSON ( default-configuration = builtins.fromJSON (
let let
env = builtins.getEnv "DEPLOYMENT"; env = builtins.getEnv "DEPLOYMENT";
@ -74,6 +75,11 @@
(import ./deployment/fediversity { (import ./deployment/fediversity {
inherit system host-mapping; inherit system host-mapping;
ancilliary.garage = "test01"; 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 // { conf."default-configuration" = default-configuration // {
enable = true; enable = true;
applications = lib.mapAttrs (_app: _: { enable = true; }) host-mapping; applications = lib.mapAttrs (_app: _: { enable = true; }) host-mapping;

View file

@ -9,6 +9,7 @@
}: }:
let let
inherit (pkgs) lib; inherit (pkgs) lib;
inherit (pkgs.callPackage ../deployment/utils.nix { }) optionalEnv;
manage = pkgs.writeScriptBin "manage" '' manage = pkgs.writeScriptBin "manage" ''
exec ${pkgs.lib.getExe pkgs.python3} ${toString ./src/manage.py} $@ exec ${pkgs.lib.getExe pkgs.python3} ${toString ./src/manage.py} $@
''; '';
@ -28,10 +29,15 @@ in
]; ];
env = { env = {
DEPLOYMENT_FLAKE = toString ../.; DEPLOYMENT_FLAKE = toString ../.;
DEPLOYMENT_NAME = "test"; DEPLOYMENT_NAME = "operator";
NPINS_DIRECTORY = toString ../npins; NPINS_DIRECTORY = toString ../npins;
CREDENTIALS_DIRECTORY = toString ./.credentials; CREDENTIALS_DIRECTORY = toString ./.credentials;
DATABASE_URL = "sqlite:///${toString ./src}/db.sqlite3"; 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 = '' shellHook = ''
${lib.concatStringsSep "\n" ( ${lib.concatStringsSep "\n" (

View file

@ -137,19 +137,6 @@ in
type = types.attrsOf types.path; type = types.attrsOf types.path;
default = { }; 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 = { deployment = {
flake = mkOption { flake = mkOption {
type = types.path; type = types.path;
@ -160,7 +147,7 @@ in
}; };
name = mkOption { name = mkOption {
type = types.str; type = types.str;
default = "test"; default = "operator";
description = '' description = ''
The name of the deployment within the flake. The name of the deployment within the flake.
''; '';
@ -213,9 +200,6 @@ in
path = [ path = [
python-environment python-environment
manage-service manage-service
## NixOps4 and its dependencies
cfg.nixops4Package
pkgs.nix pkgs.nix
pkgs.openssh pkgs.openssh
pkgs.git pkgs.git

View file

@ -8,6 +8,7 @@
sources ? import ../../npins, sources ? import ../../npins,
}: }:
let let
inherit (builtins) toFile toJSON;
src = src =
with lib.fileset; with lib.fileset;
toSource { toSource {
@ -33,7 +34,7 @@ let
let let
jsonschema = callPackage "${sources.clan-core}/lib/jsonschema" { } { }; jsonschema = callPackage "${sources.clan-core}/lib/jsonschema" { } { };
frontend-options = jsonschema.parseModule ../../deployment/options.nix; frontend-options = jsonschema.parseModule ../../deployment/options.nix;
schema = with builtins; toFile "schema.json" (toJSON frontend-options); schema = toFile "schema.json" (toJSON frontend-options);
in in
[ [
{ {
@ -69,7 +70,7 @@ python3.pkgs.buildPythonPackage {
preBuild = '' preBuild = ''
echo "recursive-include ${name} *" > MANIFEST.in echo "recursive-include ${name} *" > MANIFEST.in
cp ${builtins.toFile "source" pyproject-toml} pyproject.toml cp ${toFile "source" pyproject-toml} pyproject.toml
''; '';
propagatedBuildInputs = propagatedBuildInputs =

View file

@ -32,7 +32,7 @@ class ConfigurationForm(TestCase):
form_data = context['serializer'].instance.dict().copy() form_data = context['serializer'].instance.dict().copy()
form_data = { form_data = {
"enable": True, "enable": True,
"mastodon.enable": True, "applications.mastodon.enable": True,
} }
response = self.client.post(self.config_url, data=form_data, follow=True) response = self.client.post(self.config_url, data=form_data, follow=True)

View file

@ -90,15 +90,16 @@ class DeploymentStatus(ConfigurationForm):
def deployment(self, config: BaseModel): def deployment(self, config: BaseModel):
env = { env = {
"PATH": os.environ.get("PATH"), "PATH": os.environ.get("PATH"),
"SSH_PRIVATE_KEY_FILE": os.environ.get("SSH_PRIVATE_KEY_FILE"),
# pass in form info to our deployment # pass in form info to our deployment
"DEPLOYMENT": config.json() "DEPLOYMENT": config.json()
} }
cmd = [ cmd = [
"nixops4", "nix",
"apply", "run",
settings.deployment_name, "--impure",
f".#{settings.deployment_name}",
"--show-trace", "--show-trace",
"--no-interactive",
] ]
deployment_result = subprocess.run( deployment_result = subprocess.run(
cmd, cmd,