Files
arr-init/modules/bazarr.nix
Simon Gardling 948c9e3a38 refactor: split module.nix into per-service modules
Replace the 1301-line monolithic module.nix with focused modules:
- modules/servarr.nix  (Sonarr/Radarr/Prowlarr)
- modules/bazarr.nix   (Bazarr provider connections)
- modules/jellyseerr.nix (Jellyseerr quality profiles)
- modules/default.nix  (import aggregator)

Python scripts (from prior commit) are referenced as standalone
files via PYTHONPATH, with config passed as a JSON file argument.

New options:
- Add bindAddress option to all services (default 127.0.0.1)
- Replace hardcoded wg.service dependency with configurable
  networkNamespaceService option
- Add systemd hardening: PrivateTmp, NoNewPrivileges, ProtectHome,
  ProtectKernelTunables/Modules, ProtectControlGroups,
  RestrictSUIDSGID, SystemCallArchitectures=native

Test updates:
- Extract mock qBittorrent/SABnzbd servers into tests/lib/mocks.nix
- Fix duplicate wait_for_unit calls in integration test
2026-04-16 17:29:25 -04:00

183 lines
4.8 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.bazarrInit;
scriptDir = ../scripts;
pythonEnv = pkgs.python3.withPackages (
ps: with ps; [
pyyaml
requests
]
);
bazarrProviderModule = lib.types.submodule {
options = {
enable = lib.mkEnableOption "provider connection";
dataDir = lib.mkOption {
type = lib.types.str;
description = "Path to the provider's data directory containing config.xml.";
example = "/services/sonarr";
};
bindAddress = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1";
description = "IP address of the provider (Sonarr/Radarr) for Bazarr to connect to.";
};
port = lib.mkOption {
type = lib.types.port;
description = "API port of the provider.";
example = 8989;
};
serviceName = lib.mkOption {
type = lib.types.str;
description = "Name of the systemd service to depend on.";
example = "sonarr";
};
};
};
bazarrInitModule = lib.types.submodule {
options = {
enable = lib.mkEnableOption "Bazarr API initialization";
dataDir = lib.mkOption {
type = lib.types.str;
description = "Path to Bazarr's data directory containing config/config.yaml.";
example = "/services/bazarr";
};
bindAddress = lib.mkOption {
type = lib.types.str;
default = "127.0.0.1";
description = "IP address the Bazarr API is listening on.";
};
port = lib.mkOption {
type = lib.types.port;
default = 6767;
description = "API port of Bazarr.";
};
apiTimeout = lib.mkOption {
type = lib.types.ints.positive;
default = 90;
description = ''
Seconds to wait for the Bazarr API to become available before
considering the init attempt failed. When the API is not reachable
within this window, the service exits non-zero and systemd's
Restart=on-failure will schedule another attempt after RestartSec.
The systemd start limit is computed from this value to allow 5 full
retry cycles before the unit enters permanent failure.
'';
};
sonarr = lib.mkOption {
type = bazarrProviderModule;
default = {
enable = false;
};
description = "Sonarr provider configuration.";
};
radarr = lib.mkOption {
type = bazarrProviderModule;
default = {
enable = false;
};
description = "Radarr provider configuration.";
};
};
};
mkBazarrInitConfig = builtins.toJSON {
dataDir = cfg.dataDir;
bindAddress = cfg.bindAddress;
port = cfg.port;
apiTimeout = cfg.apiTimeout;
providers =
{ }
// lib.optionalAttrs cfg.sonarr.enable {
sonarr = {
enable = true;
dataDir = cfg.sonarr.dataDir;
bindAddress = cfg.sonarr.bindAddress;
port = cfg.sonarr.port;
};
}
// lib.optionalAttrs cfg.radarr.enable {
radarr = {
enable = true;
dataDir = cfg.radarr.dataDir;
bindAddress = cfg.radarr.bindAddress;
port = cfg.radarr.port;
};
};
};
configFile = pkgs.writeText "bazarr-init-config.json" mkBazarrInitConfig;
bazarrDeps =
[
"bazarr.service"
]
++ (lib.optional cfg.sonarr.enable "${cfg.sonarr.serviceName}.service")
++ (lib.optional cfg.radarr.enable "${cfg.radarr.serviceName}.service");
hardeningConfig = {
PrivateTmp = true;
NoNewPrivileges = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
RestrictSUIDSGID = true;
ProtectHome = true;
SystemCallArchitectures = "native";
};
in
{
options.services.bazarrInit = lib.mkOption {
type = bazarrInitModule;
default = {
enable = false;
};
description = ''
Bazarr API initialization for connecting Sonarr and Radarr providers.
Bazarr uses a different API than Servarr applications, so it has its own module.
'';
};
config = lib.mkIf cfg.enable {
systemd.services.bazarr-init = {
description = "Initialize Bazarr API connections";
after = bazarrDeps;
requires = bazarrDeps;
wantedBy = [ "multi-user.target" ];
environment.PYTHONPATH = "${scriptDir}";
unitConfig = {
StartLimitIntervalSec = 5 * (cfg.apiTimeout + 30);
StartLimitBurst = 5;
};
serviceConfig =
{
Type = "oneshot";
RemainAfterExit = true;
Restart = "on-failure";
RestartSec = 30;
ExecStart = "${pythonEnv}/bin/python3 ${scriptDir}/bazarr_init.py ${configFile}";
}
// hardeningConfig;
};
};
}