forked from Fediversity/Fediversity
pass in description fix syntax configure proxmox provider typo add doc comment in existing modules add comment allow insecure proxmox connection for use in dev wip proxmox progress use service configurations moved to machine-independent location wire settings directly without option block terraform adjust cwd try tf on null input update .envrc.sample with sample proxmox credentials
222 lines
6.7 KiB
Nix
222 lines
6.7 KiB
Nix
{
|
|
config,
|
|
pkgs,
|
|
lib,
|
|
...
|
|
}:
|
|
let
|
|
inherit (lib)
|
|
concatStringsSep
|
|
mapAttrsToList
|
|
mkDefault
|
|
mkEnableOption
|
|
mkIf
|
|
mkOption
|
|
optionalString
|
|
types
|
|
;
|
|
inherit (pkgs) writeShellApplication;
|
|
|
|
# TODO: configure the name globally for everywhere it's used
|
|
name = "panel";
|
|
|
|
cfg = config.services.${name};
|
|
package = pkgs.callPackage ./package.nix { };
|
|
|
|
environment = import ../env.nix { inherit lib pkgs; } // {
|
|
DATABASE_URL = "sqlite:////var/lib/${name}/db.sqlite3";
|
|
USER_SETTINGS_FILE = pkgs.concatText "configuration.py" [
|
|
((pkgs.formats.pythonVars { }).generate "settings.py" cfg.settings)
|
|
(builtins.toFile "extra-settings.py" cfg.extra-settings)
|
|
];
|
|
REPO_DIR = import ../../infra/tf-env.nix {
|
|
inherit lib pkgs;
|
|
};
|
|
};
|
|
|
|
python-environment = pkgs.python3.withPackages (
|
|
ps: with ps; [
|
|
package
|
|
uvicorn
|
|
]
|
|
);
|
|
|
|
manage-service = writeShellApplication {
|
|
name = "manage";
|
|
text = ''exec ${package}/bin/manage.py "$@"'';
|
|
};
|
|
|
|
manage-admin = writeShellApplication {
|
|
# This allows running the `manage` command in the system environment, e.g. to initialise an admin user
|
|
# Executing
|
|
name = "manage";
|
|
text =
|
|
''
|
|
systemd-run --pty \
|
|
--wait \
|
|
--collect \
|
|
--service-type=exec \
|
|
--unit "manage-${name}.service" \
|
|
--property "User=${name}" \
|
|
--property "Group=${name}" \
|
|
--property "WorkingDirectory=/var/lib/${name}" \
|
|
--property "Environment=''
|
|
+ (toString (lib.mapAttrsToList (name: value: "${name}=${value}") environment))
|
|
+ "\" \\\n"
|
|
+ optionalString (credentials != [ ]) (
|
|
(concatStringsSep " \\\n" (map (cred: "--property 'LoadCredential=${cred}'") credentials)) + " \\\n"
|
|
)
|
|
+ ''
|
|
${lib.getExe manage-service} "$@"
|
|
'';
|
|
};
|
|
|
|
credentials = mapAttrsToList (name: secretPath: "${name}:${secretPath}") cfg.secrets;
|
|
in
|
|
# TODO: for a more clever and generic way of running Django services:
|
|
# https://git.dgnum.eu/mdebray/djangonix/
|
|
# unlicensed at the time of writing, but surely worth taking some inspiration from...
|
|
{
|
|
options.services.${name} = {
|
|
enable = mkEnableOption "Service configuration for `${name}`";
|
|
production = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
};
|
|
restart = mkOption {
|
|
description = "systemd restart behavior";
|
|
type = types.enum [
|
|
"no"
|
|
"on-success"
|
|
"on-failure"
|
|
"on-abnormal"
|
|
"on-abort"
|
|
"always"
|
|
];
|
|
default = "always";
|
|
};
|
|
domain = mkOption { type = types.str; };
|
|
host = mkOption {
|
|
type = types.str;
|
|
default = "127.0.0.1";
|
|
};
|
|
port = mkOption {
|
|
type = types.port;
|
|
default = 8000;
|
|
};
|
|
settings = mkOption {
|
|
type = types.attrsOf types.anything;
|
|
default = {
|
|
STATIC_ROOT = mkDefault "/var/lib/${name}/static";
|
|
DEBUG = mkDefault false;
|
|
ALLOWED_HOSTS = mkDefault [
|
|
cfg.domain
|
|
cfg.host
|
|
"localhost"
|
|
"[::1]"
|
|
];
|
|
CSRF_TRUSTED_ORIGINS = mkDefault [ "https://${cfg.domain}" ];
|
|
COMPRESS_OFFLINE = true;
|
|
LIBSASS_OUTPUT_STYLE = "compressed";
|
|
};
|
|
description = ''
|
|
Django configuration as an attribute set.
|
|
Name-value pairs will be converted to Python variable assignments.
|
|
'';
|
|
};
|
|
extra-settings = mkOption {
|
|
type = types.lines;
|
|
default = "";
|
|
description = ''
|
|
Django configuration written in Python verbatim.
|
|
Contents will be appended to the definitions in `settings`.
|
|
'';
|
|
};
|
|
secrets = mkOption {
|
|
type = types.attrsOf types.path;
|
|
default = { };
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
nixpkgs.overlays = [ (import ./overlay.nix) ];
|
|
|
|
environment.systemPackages = [ manage-admin ];
|
|
|
|
services = {
|
|
nginx.enable = true;
|
|
nginx.virtualHosts = {
|
|
${cfg.domain} =
|
|
{
|
|
locations = {
|
|
"/".proxyPass = "http://localhost:${toString cfg.port}";
|
|
"/static/".alias = "/var/lib/${name}/static/";
|
|
};
|
|
}
|
|
// lib.optionalAttrs cfg.production {
|
|
enableACME = true;
|
|
forceSSL = true;
|
|
};
|
|
};
|
|
};
|
|
|
|
users.users.${name}.isNormalUser = true;
|
|
|
|
users.groups.${name} = { };
|
|
systemd.services.${name} = {
|
|
description = "${name} ASGI server";
|
|
after = [ "network.target" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
path = [
|
|
pkgs.openssh
|
|
python-environment
|
|
manage-service
|
|
];
|
|
preStart = ''
|
|
# Auto-migrate on first run or if the package has changed
|
|
versionFile="/var/lib/${name}/package-version"
|
|
if [[ $(cat "$versionFile" 2>/dev/null) != ${package} ]]; then
|
|
manage migrate --no-input
|
|
manage collectstatic --no-input --clear
|
|
manage compress --force
|
|
echo ${package} > "$versionFile"
|
|
fi
|
|
'';
|
|
script = ''
|
|
uvicorn ${name}.asgi:application --host ${cfg.host} --port ${toString cfg.port}
|
|
'';
|
|
serviceConfig = {
|
|
Restart = "always";
|
|
User = name;
|
|
WorkingDirectory = "/var/lib/${name}";
|
|
StateDirectory = name;
|
|
RuntimeDirectory = name;
|
|
LogsDirectory = name;
|
|
} // lib.optionalAttrs (credentials != [ ]) { LoadCredential = credentials; };
|
|
|
|
# TODO(@fricklerhandwerk):
|
|
# Unify handling of runtime settings.
|
|
# Right now we have four(!) places where we need to set environment variables, each in its own format:
|
|
# - Django's `settings.py` declaring the setting
|
|
# - the development environment
|
|
# - the `manage` command
|
|
# - here, the service configuration
|
|
# Ideally we'd set them in two places (development environment and service configuration) but in the same format.
|
|
#
|
|
# For that we need to take into account
|
|
# - the different types of settings
|
|
# - secrets, which must not end up in the store
|
|
# - other values, which can be world-readable
|
|
# - ergonomics
|
|
# - manipulation should be straightforward in both places; e.g. dumping secrets to a directory that is not git-tracked and adding values to an attrset otherwise
|
|
# - error detection and correction; it should be clear where and why one messed up so it can be fixed immediately
|
|
# We may also want to test the development environment in CI in order to make sure that we don't break it inadvertently, because misconfiguration due to multiplpe sources of truth wastes a lot of time.
|
|
inherit environment;
|
|
};
|
|
|
|
networking.firewall.allowedTCPPorts = [
|
|
80
|
|
443
|
|
];
|
|
};
|
|
}
|