Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/sonarr: add settings option #373576

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion nixos/modules/services/misc/sonarr.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@
}:
let
cfg = config.services.sonarr;

settingsFormat = pkgs.formats.xml { };
settingsDefault = {
Port = 8989;
BindAddress = "*";
AuthenticationMethod = "None";
UpdateMechanism = "external";
};
settingsCombined = settingsDefault // cfg.settings;

# add empty ApiKey so it can be replaced afterwards
configContent =
settingsCombined // (lib.optionalAttrs (cfg.apiKeyFile != null) { ApiKey = "@APIKEY@"; });
configFile = settingsFormat.generate "sonarr-config.xml" { Config = configContent; };
in
{
options = {
Expand All @@ -27,6 +41,31 @@ in
'';
};

settings = lib.mkOption {
inherit (settingsFormat) type;
description = "An attribute set containing Sonarr configuration settings.";
default = { };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT, changes done in the web UI is still allowed, but will be reverted on the next service start. I think that should be noted in the description field. How about

An attribute set containing Sonarr configuration written to the config.xml file, which will be applied on every service start. IOW, it overwrites imperative changes done in web UI.

I think config.xml covers roughly the stuff in Settings -> General -- should we mention it?

By reading the generated documentation, it seems the default settings are empty. But they're not. Can we have the actual default settings in the option default value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so overall you only suggest an documentation change? Or can we only write, when the config changes?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to update the description and wonder if we can put the default values right there in default = ....

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what to do about the "this affects some of the applications config" issue -- I was just thinking out loud.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
default = { };
default = settingsDefault;

example = {
LogLevel = "info";
EnableSsl = "False";
Port = 8989;
SslPort = 9898;
UrlBase = "";
BindAddress = "*";
AuthenticationMethod = "None";
UpdateMechanism = "external";
Branch = "main";
InstanceName = "Sonarr";
};
};

apiKeyFile = lib.mkOption {
type = lib.types.nullOr lib.types.path;
description = "Path to the file containing the API key for Sonarr (32 chars).";
ambroisie marked this conversation as resolved.
Show resolved Hide resolved
example = "/run/secrets/sonarr-apikey";
default = null;
};

user = lib.mkOption {
type = lib.types.str;
default = "sonarr";
Expand All @@ -44,6 +83,15 @@ in
};

config = lib.mkIf cfg.enable {
assertions = [
{
assertion = cfg.settings ? ApiKey -> false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assertion = cfg.settings ? ApiKey -> false;
assertion = !(builtins.hasAttr "ApiKey" cfg.settings);

message = ''
The `services.sonarr.settings` attribute set must not contain `ApiKey`, as it is a secret and cannot be securely stored in the Nix store.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should point to apiKeyFile in the message, how about:

Suggested change
The `services.sonarr.settings` attribute set must not contain `ApiKey`, as it is a secret and cannot be securely stored in the Nix store.
The `services.sonarr.settings` attribute set must not contain `ApiKey`, you should instead use `services.sonarr.apiKeyFile` to avoid storing secrets in the Nix store.

'';
}
];

systemd.tmpfiles.rules = [
"d '${cfg.dataDir}' 0700 ${cfg.user} ${cfg.group} - -"
];
Expand All @@ -52,11 +100,19 @@ in
description = "Sonarr";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
reloadTriggers = [ configFile ];

serviceConfig = {
Type = "simple";
User = cfg.user;
Group = cfg.group;
# set config file in pre
ExecStartPre =
[
"${pkgs.coreutils}/bin/install -o ${cfg.user} -g ${cfg.group} ${configFile} ${cfg.dataDir}/config.xml"
]
++ lib.optional (cfg.apiKeyFile != null)
"${pkgs.replace-secret}/bin/replace-secret '@APIKEY@' '${cfg.apiKeyFile}' ${cfg.dataDir}/config.xml";
ExecStart = utils.escapeSystemdExecArgs [
(lib.getExe cfg.package)
"-nobrowser"
Expand All @@ -67,7 +123,7 @@ in
};

networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts = [ 8989 ];
allowedTCPPorts = [ settingsCombined.Port ];
};

users.users = lib.mkIf (cfg.user == "sonarr") {
Expand Down
31 changes: 7 additions & 24 deletions nixos/tests/prometheus-exporters.nix
Original file line number Diff line number Diff line change
Expand Up @@ -356,41 +356,24 @@ let
'';
};

exportarr-sonarr = let apikey = "eccff6a992bc2e4b88e46d064b26bb4e"; in {
exportarr-sonarr = let apiKeyFile = pkgs.writeText "dummy-api-key" "eccff6a992bc2e4b88e46d064b26bb4e"; in {
nodeName = "exportarr_sonarr";
exporterConfig = {
enable = true;
url = "http://127.0.0.1:8989";
apiKeyFile = pkgs.writeText "dummy-api-key" apikey;
inherit apiKeyFile;
};
metricProvider = {
services.sonarr.enable = true;
systemd.services.sonarr.serviceConfig.ExecStartPre =
let
sonarr_config = pkgs.writeText "config.xml" ''
<Config>
<LogLevel>info</LogLevel>
<EnableSsl>False</EnableSsl>
<Port>8989</Port>
<SslPort>9898</SslPort>
<UrlBase></UrlBase>
<BindAddress>*</BindAddress>
<ApiKey>${apikey}</ApiKey>
<AuthenticationMethod>None</AuthenticationMethod>
<UpdateMechanism>BuiltIn</UpdateMechanism>
<Branch>main</Branch>
<InstanceName>Sonarr</InstanceName>
</Config>
'';
in
[
''${pkgs.coreutils}/bin/install -D -m 777 ${sonarr_config} -T /var/lib/sonarr/.config/NzbDrone/config.xml''
];
services.sonarr = {
enable = true;
inherit apiKeyFile;
};
};
exporterTest = ''
wait_for_unit("sonarr.service")
wait_for_open_port(8989)
wait_for_unit("prometheus-exportarr-sonarr-exporter.service")
wait_for_file("/var/lib/sonarr/.config/NzbDrone/config.xml")
wait_for_open_port(9708)
succeed("curl -sSf http://localhost:9708/metrics | grep sonarr_series_total")
'';
Expand Down