mirror of
https://git.pvv.ntnu.no/Drift/pvv-nixos-config.git
synced 2026-03-27 10:07:32 +01:00
modules/drumknotty: split into several parts
This also fixes a few issues, such as enabling `createLocalDatabase` for multiple programs, and wraps all the screen logic within a screenrc file. Some assertions were also added to avoid some easy-to-make mistakes.
This commit is contained in:
198
modules/drumknotty/default.nix
Normal file
198
modules/drumknotty/default.nix
Normal file
@@ -0,0 +1,198 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.services.drumknotty;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./dibbler.nix
|
||||
./worblehat.nix
|
||||
];
|
||||
|
||||
options.services.drumknotty = {
|
||||
enable = lib.mkEnableOption "DrumknoTTY";
|
||||
|
||||
kioskMode = lib.mkEnableOption "" // {
|
||||
description = ''
|
||||
Whether to let dibbler take over the entire machine.
|
||||
|
||||
This will restrict the machine to a single TTY and make the program unquittable.
|
||||
You can still get access to PTYs via SSH and similar, if enabled.
|
||||
'';
|
||||
};
|
||||
|
||||
screen = {
|
||||
package = lib.mkPackageOption pkgs "screen" { };
|
||||
|
||||
sessionName = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "drumknotty";
|
||||
example = "myscreensessionname";
|
||||
description = ''
|
||||
Sets the screen session name.
|
||||
'';
|
||||
};
|
||||
|
||||
limitHeight = lib.mkOption {
|
||||
type = with lib.types; nullOr ints.unsigned;
|
||||
default = null;
|
||||
example = 42;
|
||||
description = ''
|
||||
If set, limits the height of the screen dibbler uses to the given number of lines.
|
||||
'';
|
||||
};
|
||||
|
||||
limitWidth = lib.mkOption {
|
||||
type = with lib.types; nullOr ints.unsigned;
|
||||
default = null;
|
||||
example = 80;
|
||||
description = ''
|
||||
If set, limits the width of the screen dibbler uses to the given number of columns.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.enable -> lib.any (b: b) [
|
||||
cfg.dibbler.enable
|
||||
cfg.worblehat.enable
|
||||
];
|
||||
message = "DrumknoTTY must have at least one service enabled";
|
||||
}
|
||||
];
|
||||
|
||||
users = {
|
||||
users.drumknotty = {
|
||||
group = "drumknotty";
|
||||
extraGroups = [ "lp" ];
|
||||
isNormalUser = true;
|
||||
|
||||
# TODO: make this display the error log or error message in case that
|
||||
# the screen session service is bootlooping or otherwise off.
|
||||
shell =
|
||||
lib.mkIf cfg.kioskMode
|
||||
(pkgs.writeShellScriptBin "login-shell"
|
||||
"${lib.getExe' cfg.screen.package "screen"} -x ${cfg.screen.sessionName} -p dibbler"
|
||||
// {
|
||||
shellPath = "/bin/login-shell";
|
||||
});
|
||||
};
|
||||
groups.drumknotty = { };
|
||||
};
|
||||
|
||||
boot.kernelParams = lib.mkIf cfg.kioskMode [
|
||||
"console=tty1"
|
||||
];
|
||||
|
||||
services.getty.autologinUser = lib.mkIf cfg.kioskMode "drumknotty";
|
||||
|
||||
systemd.services.drumknotty-screen-session = lib.mkIf cfg.kioskMode {
|
||||
description = "Drumknotty Screen Session";
|
||||
wantedBy = [
|
||||
"default.target"
|
||||
];
|
||||
after =
|
||||
# TODO: this could be refined
|
||||
if (cfg.dibbler.createLocalDatabase || cfg.worblehat.createLocalDatabase) then
|
||||
[
|
||||
"postgresql.service"
|
||||
"dibbler-setup-database.service"
|
||||
"worblehat-setup-database.service"
|
||||
]
|
||||
else
|
||||
[
|
||||
"network.target"
|
||||
];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "forking";
|
||||
RemainAfterExit = false;
|
||||
Restart = "always";
|
||||
RestartSec = "5s";
|
||||
SuccessExitStatus = 1;
|
||||
|
||||
User = "drumknotty";
|
||||
Group = "drumknotty";
|
||||
|
||||
ExecStartPre =
|
||||
let
|
||||
screenArgs = lib.escapeShellArgs [
|
||||
# Send the specified command to a running screen session
|
||||
"-X"
|
||||
|
||||
# Session name
|
||||
"-S"
|
||||
"${cfg.screen.sessionName}"
|
||||
|
||||
"kill"
|
||||
];
|
||||
in
|
||||
"-${lib.getExe' cfg.screen.package "screen"} ${screenArgs}";
|
||||
|
||||
ExecStart =
|
||||
let
|
||||
screenrc = let
|
||||
convertToFile = lines: lib.pipe lines [
|
||||
lib.concatLists
|
||||
(lib.concatStringsSep "\n")
|
||||
(pkgs.writeText "drumknotty-screenrc")
|
||||
];
|
||||
in convertToFile [
|
||||
(lib.optionals (cfg.screen.limitWidth != null) [
|
||||
"screen width ${toString cfg.screen.limitWidth}"
|
||||
])
|
||||
(lib.optionals (cfg.screen.limitHeight != null) [
|
||||
"screen height ${toString cfg.screen.limitHeight}"
|
||||
])
|
||||
|
||||
(let
|
||||
dibblerArgs = lib.cli.toCommandLineShellGNU { } {
|
||||
config = "/etc/dibbler/dibbler.toml";
|
||||
};
|
||||
in lib.optionals cfg.dibbler.enable [
|
||||
"screen -t worblehat ${lib.getExe cfg.dibbler.package} ${dibblerArgs} loop"
|
||||
|
||||
])
|
||||
|
||||
(let
|
||||
worblehatArgs = lib.cli.toCommandLineShellGNU { } {
|
||||
config = "/etc/worblehat/config.toml";
|
||||
};
|
||||
in lib.optionals cfg.worblehat.enable [
|
||||
"screen -t worblehat ${lib.getExe cfg.worblehat.package} ${worblehatArgs} cli"
|
||||
])
|
||||
|
||||
[ "select 0" ]
|
||||
];
|
||||
|
||||
screenArgs = lib.escapeShellArgs [
|
||||
# -dm creates the screen in detached mode without accessing it
|
||||
"-dm"
|
||||
|
||||
# Session name
|
||||
"-S"
|
||||
"${cfg.screen.sessionName}"
|
||||
|
||||
# Set optimal output mode instead of VT100 emulation
|
||||
"-O"
|
||||
|
||||
# Enable login mode, updates utmp entries
|
||||
"-l"
|
||||
|
||||
# Config file path
|
||||
"-c"
|
||||
"${screenrc}"
|
||||
];
|
||||
in
|
||||
"${lib.getExe' cfg.screen.package "screen"} ${screenArgs}";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
113
modules/drumknotty/dibbler.nix
Normal file
113
modules/drumknotty/dibbler.nix
Normal file
@@ -0,0 +1,113 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
mainCfg = config.services.drumknotty;
|
||||
cfg = config.services.drumknotty.dibbler;
|
||||
|
||||
format = pkgs.formats.toml { };
|
||||
in
|
||||
{
|
||||
options.services.drumknotty.dibbler = {
|
||||
enable = lib.mkEnableOption "";
|
||||
|
||||
package = lib.mkPackageOption pkgs "dibbler" { };
|
||||
|
||||
settings = lib.mkOption {
|
||||
description = "Configuration for dibbler";
|
||||
default = { };
|
||||
type = lib.types.submodule {
|
||||
freeformType = format.type;
|
||||
};
|
||||
};
|
||||
|
||||
createLocalDatabase = lib.mkEnableOption "" // {
|
||||
description = ''
|
||||
Whether to set up a local postgres database automatically.
|
||||
|
||||
::: {.note}
|
||||
You must set up postgres manually before enabling this option.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf (mainCfg.enable && cfg.enable) {
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.createLocalDatabase -> config.services.postgresql.enable;
|
||||
message = "PostgreSQL must be enabled for dibbler to create a local database";
|
||||
}
|
||||
];
|
||||
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
environment.etc."dibbler/dibbler.toml".source = format.generate "dibbler.toml" cfg.settings;
|
||||
|
||||
services.drumknotty.dibbler.settings = {
|
||||
limits = {
|
||||
low_credit_warning_limit = lib.mkDefault (-100);
|
||||
user_recent_transaction_limit = lib.mkDefault 100;
|
||||
};
|
||||
|
||||
printer = {
|
||||
label_type = lib.mkDefault "62";
|
||||
label_rotate = lib.mkDefault false;
|
||||
};
|
||||
|
||||
database = {
|
||||
type = lib.mkIf cfg.createLocalDatabase "postgresql";
|
||||
postgresql = {
|
||||
username = lib.mkDefault "dibbler";
|
||||
dbname = lib.mkDefault "dibbler";
|
||||
|
||||
host = lib.mkIf cfg.createLocalDatabase "/run/postgresql";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.drumknotty.dibbler.settings.general = lib.mkIf mainCfg.kioskMode {
|
||||
quit_allowed = false;
|
||||
stop_allowed = false;
|
||||
};
|
||||
|
||||
services.postgresql = lib.mkIf cfg.createLocalDatabase {
|
||||
authentication = ''
|
||||
local ${cfg.settings.database.postgresql.dbname} ${cfg.settings.database.postgresql.username} peer map=${cfg.settings.database.postgresql.username}
|
||||
'';
|
||||
identMap = ''
|
||||
${cfg.settings.database.postgresql.username} drumknotty ${cfg.settings.database.postgresql.username}
|
||||
'';
|
||||
ensureDatabases = [ cfg.settings.database.postgresql.dbname ];
|
||||
ensureUsers = [{
|
||||
name = cfg.settings.database.postgresql.username;
|
||||
ensureDBOwnership = true;
|
||||
ensureClauses.login = true;
|
||||
}];
|
||||
};
|
||||
|
||||
systemd.services.dibbler-setup-database = lib.mkIf cfg.createLocalDatabase {
|
||||
description = "Dibbler database setup";
|
||||
|
||||
wantedBy = [ "default.target" ];
|
||||
requiredBy = [ "drumknotty-screen-session.service" ];
|
||||
before = [ "drumknotty-screen-session.service" ];
|
||||
after = [ "postgresql.service" ];
|
||||
|
||||
unitConfig = {
|
||||
ConditionPathExists = "!/var/lib/dibbler/.db-setup-done";
|
||||
};
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${lib.getExe cfg.package} --config /etc/dibbler/dibbler.toml create-db";
|
||||
ExecStartPost = "${lib.getExe' pkgs.coreutils "touch"} /var/lib/dibbler/.db-setup-done";
|
||||
StateDirectory = "dibbler";
|
||||
|
||||
User = "drumknotty";
|
||||
Group = "drumknotty";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
209
modules/drumknotty/worblehat.nix
Normal file
209
modules/drumknotty/worblehat.nix
Normal file
@@ -0,0 +1,209 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
mainCfg = config.services.drumknotty;
|
||||
cfg = config.services.drumknotty.worblehat;
|
||||
|
||||
format = pkgs.formats.toml { };
|
||||
in
|
||||
{
|
||||
options.services.drumknotty.worblehat = {
|
||||
enable = lib.mkEnableOption "";
|
||||
|
||||
package = lib.mkPackageOption pkgs "worblehat" { };
|
||||
|
||||
settings = lib.mkOption {
|
||||
description = "Configuration for worblehat";
|
||||
default = { };
|
||||
type = lib.types.submodule {
|
||||
freeformType = format.type;
|
||||
};
|
||||
};
|
||||
|
||||
createLocalDatabase = lib.mkEnableOption "" // {
|
||||
description = ''
|
||||
Whether to set up a local postgres database automatically.
|
||||
|
||||
::: {.note}
|
||||
You must set up postgres manually before enabling this option.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
||||
deadline-daemon = {
|
||||
enable = lib.mkEnableOption "" // {
|
||||
description = ''
|
||||
Whether to enable the worblehat deadline-daemon service,
|
||||
which periodically checks for upcoming deadlines and notifies users.
|
||||
|
||||
Note that this service is independent of the main worblehat service,
|
||||
and must be enabled separately.
|
||||
'';
|
||||
};
|
||||
|
||||
onCalendar = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = ''
|
||||
How often to trigger rendering the map,
|
||||
in the format of a systemd timer onCalendar configuration.
|
||||
|
||||
See {manpage}`systemd.timer(5)`.
|
||||
'';
|
||||
default = "*-*-* 10:15:00";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
{
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.createLocalDatabase -> config.services.postgresql.enable;
|
||||
message = "PostgreSQL must be enabled for worblehat to create a local database";
|
||||
}
|
||||
];
|
||||
|
||||
# TODO: Retrieve defaults from the example config file in the project code.
|
||||
services.drumknotty.worblehat.settings = {
|
||||
logging = {
|
||||
debug = lib.mkDefault true;
|
||||
debug_sql = lib.mkDefault false;
|
||||
};
|
||||
|
||||
database = {
|
||||
type = lib.mkDefault "sqlite";
|
||||
sqlite.path = lib.mkDefault "./worblehat.sqlite";
|
||||
postgresql = {
|
||||
host = lib.mkDefault "localhost";
|
||||
port = lib.mkDefault 5432;
|
||||
username = lib.mkDefault "worblehat";
|
||||
password = lib.mkDefault "/var/lib/worblehat/db-password";
|
||||
database = lib.mkDefault "worblehat";
|
||||
};
|
||||
};
|
||||
|
||||
flask = {
|
||||
TESTING = lib.mkDefault true;
|
||||
DEBUG = lib.mkDefault true;
|
||||
FLASK_ENV = lib.mkDefault "development";
|
||||
SECRET_KEY = lib.mkDefault "change-me";
|
||||
};
|
||||
|
||||
smtp = {
|
||||
enabled = lib.mkDefault false;
|
||||
host = lib.mkDefault "smtp.pvv.ntnu.no";
|
||||
port = lib.mkDefault 587;
|
||||
username = lib.mkDefault "worblehat";
|
||||
password = lib.mkDefault "/var/lib/worblehat/smtp-password";
|
||||
from = lib.mkDefault "worblehat@pvv.ntnu.no";
|
||||
subject_prefix = lib.mkDefault "[Worblehat]";
|
||||
};
|
||||
|
||||
deadline_daemon = {
|
||||
enabled = lib.mkDefault true;
|
||||
dryrun = lib.mkDefault false;
|
||||
warn_days_before_borrowing_deadline = lib.mkDefault [
|
||||
5
|
||||
1
|
||||
];
|
||||
days_before_queue_position_expires = lib.mkDefault 14;
|
||||
warn_days_before_expiring_queue_position_deadline = lib.mkDefault [
|
||||
3
|
||||
1
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
(lib.mkIf ((mainCfg.enable && cfg.enable) || cfg.deadline-daemon.enable) {
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
environment.etc."worblehat/config.toml".source = format.generate "worblehat-config.toml" cfg.settings;
|
||||
})
|
||||
|
||||
(lib.mkIf (mainCfg.enable && cfg.enable) {
|
||||
services.drumknotty.worblehat.settings.general = lib.mkIf mainCfg.kioskMode {
|
||||
quit_allowed = false;
|
||||
stop_allowed = false;
|
||||
};
|
||||
|
||||
services.drumknotty.worblehat.settings.database = lib.mkIf cfg.createLocalDatabase {
|
||||
type = "postgresql";
|
||||
postgresql.host = "/run/postgresql";
|
||||
};
|
||||
|
||||
services.postgresql = lib.mkIf cfg.createLocalDatabase {
|
||||
authentication = ''
|
||||
local ${cfg.settings.database.postgresql.database} ${cfg.settings.database.postgresql.username} peer map=${cfg.settings.database.postgresql.username}
|
||||
'';
|
||||
identMap = ''
|
||||
${cfg.settings.database.postgresql.username} drumknotty ${cfg.settings.database.postgresql.username}
|
||||
'';
|
||||
ensureDatabases = [ cfg.settings.database.postgresql.database ];
|
||||
ensureUsers = [{
|
||||
name = cfg.settings.database.postgresql.username;
|
||||
ensureDBOwnership = true;
|
||||
ensureClauses.login = true;
|
||||
}];
|
||||
};
|
||||
|
||||
systemd.services.worblehat-setup-database = lib.mkIf cfg.createLocalDatabase {
|
||||
description = "Worblehat database setup";
|
||||
|
||||
wantedBy = [ "default.target" ];
|
||||
requiredBy = [ "drumknotty-screen-session.service" ];
|
||||
before = [ "drumknotty-screen-session.service" ];
|
||||
after = [ "postgresql.service" ];
|
||||
|
||||
unitConfig = {
|
||||
ConditionPathExists = "!/var/lib/worblehat/.db-setup-done";
|
||||
};
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${lib.getExe cfg.package} --config /etc/worblehat/config.toml create-db";
|
||||
ExecStartPost = "${lib.getExe' pkgs.coreutils "touch"} /var/lib/worblehat/.db-setup-done";
|
||||
StateDirectory = "worblehat";
|
||||
|
||||
User = "drumknotty";
|
||||
Group = "drumknotty";
|
||||
};
|
||||
};
|
||||
})
|
||||
|
||||
(lib.mkIf cfg.deadline-daemon.enable {
|
||||
systemd.timers.worblehat-deadline-daemon = lib.mkIf cfg.deadline-daemon.enable {
|
||||
description = "Worblehat Deadline Daemon";
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
OnCalendar = cfg.deadline-daemon.onCalendar;
|
||||
Persistent = true;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.worblehat-deadline-daemon = lib.mkIf cfg.deadline-daemon.enable {
|
||||
description = "Worblehat Deadline Daemon";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
CPUSchedulingPolicy = "idle";
|
||||
IOSchedulingClass = "idle";
|
||||
|
||||
ExecStart =
|
||||
let
|
||||
worblehatArgs = lib.cli.toCommandLineShellGNU { } {
|
||||
config = "/etc/worblehat/config.toml";
|
||||
};
|
||||
in
|
||||
"${lib.getExe cfg.package} ${worblehatArgs} deadline-daemon";
|
||||
|
||||
User = "drumknotty";
|
||||
Group = "drumknotty";
|
||||
};
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user