nixos/modules/services/peertube-runner.nix

257 lines
8.6 KiB
Nix

{
lib,
pkgs,
config,
...
}:
let
cfg = config.services.peertube-runner;
settingsFormat = pkgs.formats.toml { };
configFile = settingsFormat.generate "config.toml" cfg.settings;
env = {
NODE_ENV = "production";
XDG_CONFIG_HOME = "/var/lib/peertube-runner";
XDG_CACHE_HOME = "/var/cache/peertube-runner";
# peertube-runner makes its IPC socket in $XDG_DATA_HOME.
XDG_DATA_HOME = "/run/peertube-runner";
};
in
{
options.services.peertube-runner = {
enable = lib.mkEnableOption "peertube-runner";
package = lib.mkPackageOption pkgs [ "peertube" "runner" ] { };
user = lib.mkOption {
type = lib.types.str;
default = "prunner";
example = "peertube-runner";
description = "User account under which peertube-runner runs.";
};
group = lib.mkOption {
type = lib.types.str;
default = "prunner";
example = "peertube-runner";
description = "Group under which peertube-runner runs.";
};
settings = lib.mkOption {
type = settingsFormat.type;
default = { };
example = lib.literalExpression ''
{
jobs.concurrency = 4;
ffmpeg = {
threads = 0; # Let ffmpeg automatically choose.
nice = 5;
};
transcription.model = "large-v3";
}
'';
description = ''
Configuration for peertube-runner.
See available configuration options at https://docs.joinpeertube.org/maintain/tools#configuration.
'';
};
instancesToRegister = lib.mkOption {
type =
with lib.types;
attrsOf (submodule {
options = {
url = lib.mkOption {
type = lib.types.str;
example = "https://mypeertubeinstance.com";
description = "URL of the PeerTube instance.";
};
registrationTokenFile = lib.mkOption {
type = lib.types.path;
example = "/run/secrets/my-peertube-instance-registration-token";
description = ''
Path to a file containing a registration token for the PeerTube instance.
See how to generate registration tokens at https://docs.joinpeertube.org/admin/remote-runners#manage-remote-runners.
'';
};
runnerName = lib.mkOption {
type = lib.types.str;
example = "Transcription";
description = "Runner name declared to the PeerTube instance.";
};
runnerDescription = lib.mkOption {
type = with lib.types; nullOr str;
default = null;
example = "Runner for video transcription";
description = "Runner description declared to the PeerTube instance.";
};
};
});
default = { };
example = {
personal = {
url = "https://mypeertubeinstance.com";
registrationTokenFile = "/run/secrets/my-peertube-instance-registration-token";
runnerName = "Transcription";
runnerDescription = "Runner for video transcription";
};
};
description = "PeerTube instances to register this runner with.";
};
enabledJobTypes = lib.mkOption {
type = with lib.types; nonEmptyListOf str;
default = [
"vod-web-video-transcoding"
"vod-hls-transcoding"
"vod-audio-merge-transcoding"
"live-rtmp-hls-transcoding"
"video-studio-transcoding"
"video-transcription"
];
example = [ "video-transcription" ];
description = "Job types that this runner will execute.";
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = !(cfg.settings ? registeredInstances);
message = ''
`services.peertube-runner.settings.registeredInstances` cannot be used.
Instead, registered instances can be configured with `services.peertube-runner.instancesToRegister`.
'';
}
];
warnings = lib.optional (cfg.instancesToRegister == { }) ''
`services.peertube-runner.instancesToRegister` is empty.
Instances cannot be manually registered using the command line.
'';
services.peertube-runner.settings = {
transcription = lib.mkIf (lib.elem "video-transcription" cfg.enabledJobTypes) {
engine = lib.mkDefault "whisper-ctranslate2";
enginePath = lib.mkDefault (lib.getExe pkgs.whisper-ctranslate2);
};
};
environment.systemPackages = [
(pkgs.writeShellScriptBin "peertube-runner" ''
${lib.concatMapAttrsStringSep "\n" (name: value: ''export ${name}="${toString value}"'') env}
if [[ "$USER" == ${cfg.user} ]]; then
exec ${lib.getExe' cfg.package "peertube-runner"} "$@"
else
echo "This has to be run with the \`${cfg.user}\` user. Ex: \`sudo -u ${cfg.user} peertube-runner\`"
fi
'')
];
systemd.services.peertube-runner = {
description = "peertube-runner daemon";
after = [
"network.target"
(lib.mkIf config.services.peertube.enable "peertube.service")
];
wantedBy = [ "multi-user.target" ];
environment = env;
path = [ pkgs.ffmpeg-headless ];
script = ''
config_dir=$XDG_CONFIG_HOME/peertube-runner-nodejs/default
mkdir -p $config_dir
config_file=$config_dir/config.toml
cp -f --no-preserve=mode,ownership ${configFile} $config_file
${lib.optionalString ((lib.length (lib.attrNames cfg.instancesToRegister)) > 0) ''
# Temp config directory for registration commands
temp_dir=$(mktemp --directory)
temp_config_dir=$temp_dir/peertube-runner-nodejs/default
mkdir -p $temp_config_dir
temp_config_file=$temp_config_dir/config.toml
mkdir -p $STATE_DIRECTORY/runner_tokens
${lib.concatMapAttrsStringSep "\n" (instanceName: instance: ''
runner_token_file=$STATE_DIRECTORY/runner_tokens/${instanceName}
# Register any currenctly unregistered instances.
if [ ! -f $runner_token_file ] || [[ $(cat $runner_token_file) != ptrt-* ]]; then
# Server has to be running for registration.
XDG_CONFIG_HOME=$temp_dir ${lib.getExe' cfg.package "peertube-runner"} server &
XDG_CONFIG_HOME=$temp_dir ${lib.getExe' cfg.package "peertube-runner"} register \
--url ${lib.escapeShellArg instance.url} \
--registration-token "$(cat ${instance.registrationTokenFile})" \
--runner-name ${lib.escapeShellArg instance.runnerName} \
${lib.optionalString (
instance.runnerDescription != null
) ''--runner-description ${lib.escapeShellArg instance.runnerDescription}''}
# Kill the server
kill $!
${lib.getExe pkgs.yq-go} -e ".registeredInstances[0].runnerToken" \
$temp_config_file > $runner_token_file
rm $temp_config_file
fi
echo "
[[registeredInstances]]
url = \"${instance.url}\"
runnerToken = \"$(cat $runner_token_file)\"
runnerName = \"${instance.runnerName}\"
${lib.optionalString (
instance.runnerDescription != null
) ''runnerDescription = \"${instance.runnerDescription}\"''}
" >> $config_file
'') cfg.instancesToRegister}
''}
# Don't allow changes that won't persist.
chmod 440 $config_file
systemd-notify --ready
exec ${lib.getExe' cfg.package "peertube-runner"} server ${
lib.concatMapStringsSep " " (jobType: "--enable-job ${jobType}") cfg.enabledJobTypes
}
'';
serviceConfig = {
Type = "notify";
NotifyAccess = "all"; # for systemd-notify
Restart = "always";
RestartSec = 5;
SyslogIdentifier = "prunner";
User = cfg.user;
Group = cfg.group;
StateDirectory = "peertube-runner";
StateDirectoryMode = "0700";
CacheDirectory = "peertube-runner";
CacheDirectoryMode = "0700";
RuntimeDirectory = "peertube-runner";
RuntimeDirectoryMode = "0700";
ProtectSystem = "full";
NoNewPrivileges = true;
ProtectHome = true;
CapabilityBoundingSet = "~CAP_SYS_ADMIN";
};
};
users.users = lib.mkIf (cfg.user == "prunner") {
${cfg.user} = {
isSystemUser = true;
group = cfg.group;
};
};
users.groups = lib.mkIf (cfg.group == "prunner") {
${cfg.group} = { };
};
};
meta.maintainers = lib.teams.ngi.members;
}