1{ pkgs, lib, ... }:
2{
3 name = "qbittorrent";
4
5 meta = with pkgs.lib.maintainers; {
6 maintainers = [
7 fsnkty
8 undefined-landmark
9 ];
10 };
11
12 nodes = {
13 simple = {
14 services.qbittorrent.enable = true;
15
16 specialisation.portChange.configuration = {
17 services.qbittorrent = {
18 enable = true;
19 webuiPort = 5555;
20 torrentingPort = 44444;
21 };
22 };
23
24 specialisation.openPorts.configuration = {
25 services.qbittorrent = {
26 enable = true;
27 openFirewall = true;
28 webuiPort = 8080;
29 torrentingPort = 55555;
30 };
31 };
32
33 specialisation.serverConfig.configuration = {
34 services.qbittorrent = {
35 enable = true;
36 webuiPort = null;
37 serverConfig.Preferences.WebUI.Port = "8181";
38 };
39 };
40 };
41 # Seperate vm because it's not possible to reboot into a specialisation with
42 # switch-to-configuration: https://github.com/NixOS/nixpkgs/issues/82851
43 # For one of the test we check if manual changes are overridden during
44 # reboot, therefore it's necessary to reboot into a declarative setup.
45 declarative = {
46 services.qbittorrent = {
47 enable = true;
48 webuiPort = null;
49 serverConfig = {
50 Preferences = {
51 WebUI = {
52 Username = "user";
53 # Default password: adminadmin
54 Password_PBKDF2 = "@ByteArray(6DIf26VOpTCYbgNiO6DAFQ==:e6241eaAWGzRotQZvVA5/up9fj5wwSAThLgXI2lVMsYTu1StUgX9MgmElU3Sa/M8fs+zqwZv9URiUOObjqJGNw==)";
55 Port = lib.mkDefault "8181";
56 };
57 };
58 };
59 };
60
61 specialisation.serverConfigChange.configuration = {
62 services.qbittorrent = {
63 enable = true;
64 webuiPort = null;
65 serverConfig.Preferences.WebUI.Port = "7171";
66 };
67 };
68 };
69 };
70
71 testScript =
72 { nodes, ... }:
73 let
74 simpleSpecPath = "${nodes.simple.system.build.toplevel}/specialisation";
75 declarativeSpecPath = "${nodes.declarative.system.build.toplevel}/specialisation";
76 portChange = "${simpleSpecPath}/portChange";
77 openPorts = "${simpleSpecPath}/openPorts";
78 serverConfig = "${simpleSpecPath}/serverConfig";
79 serverConfigChange = "${declarativeSpecPath}/serverConfigChange";
80 in
81 ''
82 simple.start(allow_reboot=True)
83 declarative.start(allow_reboot=True)
84
85
86 def test_webui(machine, port):
87 machine.wait_for_unit("qbittorrent.service")
88 machine.wait_for_open_port(port)
89 machine.wait_until_succeeds(f"curl --fail http://localhost:{port}")
90
91
92 # To simulate an interactive change in the settings
93 def setPreferences_api(machine, port, post_creds, post_data):
94 qb_url = f"http://localhost:{port}"
95 api_url = f"{qb_url}/api/v2"
96 cookie_path = "/tmp/qbittorrent.cookie"
97
98 machine.succeed(
99 f'curl --header "Referer: {qb_url}" \
100 --data "{post_creds}" {api_url}/auth/login \
101 -c {cookie_path}'
102 )
103 machine.succeed(
104 f'curl --header "Referer: {qb_url}" \
105 --data "{post_data}" {api_url}/app/setPreferences \
106 -b {cookie_path}'
107 )
108
109
110 # A randomly generated password is printed in the service log when no
111 # password it set
112 def get_temp_pass(machine):
113 _, password = machine.execute(
114 "journalctl -u qbittorrent.service |\
115 grep 'The WebUI administrator password was not set.' |\
116 awk '{ print $NF }' | tr -d '\n'"
117 )
118 return password
119
120
121 # Non declarative tests
122
123 with subtest("webui works with all default settings"):
124 test_webui(simple, 8080)
125
126 with subtest("check if manual changes in settings are saved correctly"):
127 temp_pass = get_temp_pass(simple)
128
129 ## Change some settings
130 api_post = [r"json={\"listen_port\": 33333}", r"json={\"web_ui_port\": 9090}"]
131 for x in api_post:
132 setPreferences_api(
133 machine=simple,
134 port=8080,
135 post_creds=f"username=admin&password={temp_pass}",
136 post_data=x,
137 )
138
139 simple.wait_for_open_port(33333)
140 test_webui(simple, 9090)
141
142 ## Test which settings are reset
143 ## As webuiPort is passed as an cli it should reset after reboot
144 ## As torrentingPort is not passed as an cli it should not reset after
145 ## reboot
146 simple.reboot()
147 test_webui(simple, 8080)
148 simple.wait_for_open_port(33333)
149
150 with subtest("ports are changed on config change"):
151 simple.succeed("${portChange}/bin/switch-to-configuration test")
152 test_webui(simple, 5555)
153 simple.wait_for_open_port(44444)
154
155 with subtest("firewall is opened correctly"):
156 simple.succeed("${openPorts}/bin/switch-to-configuration test")
157 test_webui(simple, 8080)
158 declarative.wait_until_succeeds("curl --fail http://simple:8080")
159 declarative.wait_for_open_port(55555, "simple")
160
161 with subtest("switching from simple to declarative works"):
162 simple.succeed("${serverConfig}/bin/switch-to-configuration test")
163 test_webui(simple, 8181)
164
165
166 # Declarative tests
167
168 with subtest("serverConfig is applied correctly"):
169 test_webui(declarative, 8181)
170
171 with subtest("manual changes are overridden during reboot"):
172 ## Change some settings
173 setPreferences_api(
174 machine=declarative,
175 port=8181, # as set through serverConfig
176 post_creds="username=user&password=adminadmin",
177 post_data=r"json={\"web_ui_port\": 9191}",
178 )
179
180 test_webui(declarative, 9191)
181
182 ## Test which settings are reset
183 ## The generated qBittorrent.conf is, apparently, reapplied after reboot.
184 ## Because the port is set in `serverConfig` this overrides the manually
185 ## set port.
186 declarative.reboot()
187 test_webui(declarative, 8181)
188
189 with subtest("changes in serverConfig are applied correctly"):
190 declarative.succeed("${serverConfigChange}/bin/switch-to-configuration test")
191 test_webui(declarative, 7171)
192 '';
193}