Skip to content

Commit 3ec7da0

Browse files
authored
Merge pull request #188 from Bastian-Krause/bst/rework-partial-downloads
test: rework nginx partial download fixture, move nginx config snippets to dedicated files
2 parents 654d991 + 7488f19 commit 3ec7da0

5 files changed

Lines changed: 103 additions & 63 deletions

File tree

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343

4444
- name: Install test dependencies
4545
run: |
46-
sudo apt-get install libcairo2-dev libgirepository1.0-dev nginx-full
46+
sudo apt-get install libcairo2-dev libgirepository1.0-dev nginx-full libnginx-mod-http-ndk libnginx-mod-http-lua
4747
python -m pip install --upgrade pip
4848
pip install -r test-requirements.txt
4949

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ Test Suite
101101
Prepare test suite:
102102

103103
```shell
104-
$ sudo apt install libcairo2-dev libgirepository1.0-dev nginx-full
104+
$ sudo apt install libcairo2-dev libgirepository1.0-dev nginx-full libnginx-mod-http-ndk libnginx-mod-http-lua
105105
$ python3 -m venv venv
106106
$ source venv/bin/activate
107107
(venv) $ pip install --upgrade pip

test/conftest.py

Lines changed: 25 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import os
66
import sys
77
from configparser import ConfigParser
8+
from string import Template
89

910
import pytest
1011

@@ -234,53 +235,6 @@ def nginx_config(tmp_path_factory, pki_dir):
234235
Creates a temporary nginx proxy configuration incorporating additional given options to the
235236
location section. See https://eclipse.dev/hawkbit/concepts/authentication/ for examples.
236237
"""
237-
config_template = """
238-
daemon off;
239-
pid /tmp/hawkbit-nginx-{port}.pid;
240-
241-
# non-fatal alert for /var/log/nginx/error.log will still be shown
242-
# https://trac.nginx.org/nginx/ticket/147
243-
error_log stderr notice;
244-
245-
events {{ }}
246-
247-
http {{
248-
access_log /dev/null;
249-
250-
map $ssl_client_s_dn $ssl_client_s_dn_cn {{
251-
default "";
252-
~CN=(?<CN>[^,]+) $CN;
253-
}}
254-
255-
server {{
256-
listen 127.0.0.1:{port} {ssl};
257-
listen [::1]:{port} {ssl};
258-
259-
ssl_certificate pki/root-ca.crt;
260-
ssl_certificate_key pki/root-ca.key;
261-
ssl_client_certificate pki/root-ca.crt;
262-
{server_options}
263-
264-
location ~*/.*/controller/ {{
265-
proxy_pass http://localhost:8080;
266-
267-
proxy_set_header Host $http_host;
268-
proxy_set_header X-Real-IP $remote_addr;
269-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
270-
proxy_set_header X-Forwarded-Proto $scheme;
271-
proxy_set_header X-Forwarded-Protocol $scheme;
272-
proxy_set_header X-Forwarded-Port {port};
273-
274-
proxy_set_header X-Ssl-Client-Cn $ssl_client_s_dn_cn;
275-
276-
# These are required for clients to upload and download software.
277-
proxy_request_buffering off;
278-
client_max_body_size 1000m;
279-
{location_options}
280-
}}
281-
}}
282-
}}"""
283-
284238
def _to_nginx_option(option):
285239
key_values = (f'{key} {value};' for key, value in option.items())
286240
return ' '.join(key_values)
@@ -294,24 +248,29 @@ def _nginx_config(port, location_options, *, mtls=False):
294248
nginx_temp = tmp_path_factory.mktemp('nginx')
295249
proxy_config = nginx_temp / 'nginx.conf'
296250
os.symlink(pki_dir, nginx_temp / 'pki')
297-
proxy_config_str = config_template.format(
298-
ssl='ssl' if mtls else '',
299-
port=port,
300-
location_options=_to_nginx_option(location_options),
301-
server_options=_to_nginx_option(server_options),
251+
252+
with open(f'{os.path.dirname(os.path.abspath(__file__))}/nginx/base.conf.in') as f:
253+
base_config_template = Template(f.read())
254+
255+
proxy_config_str = base_config_template.substitute(
256+
ssl='ssl' if mtls else '',
257+
port=port,
258+
location_options=_to_nginx_option(location_options),
259+
server_options=_to_nginx_option(server_options),
260+
module_path=os.environ.get('NGINX_MODULES', '/usr/lib/nginx/modules'),
302261
)
303262
proxy_config.write_text(proxy_config_str)
263+
304264
return proxy_config
305265

306266
return _nginx_config
307267

308268
@pytest.fixture(scope='session')
309269
def nginx_proxy(nginx_config):
310270
"""
311-
Runs an nginx rate liming proxy, limiting download speeds to 70 KB/s. HTTP requests are
312-
forwarded to port 8080 (default port of the docker hawkBit instance). Returns the port the
313-
proxy is running on. This port can be set in the rauc-hawkbit-updater config to rate limit its
314-
HTTP requests.
271+
Runs an nginx proxy. HTTP requests are forwarded to port 8080 (default port of the docker
272+
hawkBit instance). Returns the port the proxy is running on. This port can be set in the
273+
rauc-hawkbit-updater config to proxy HTTP requests with custom options.
315274
"""
316275
import pexpect
317276

@@ -363,16 +322,21 @@ def _rate_limited_port(rate):
363322
return _rate_limited_port
364323

365324
@pytest.fixture(scope='session')
366-
def partial_download_port(nginx_proxy):
325+
def partial_download_port(tmp_path_factory, rauc_bundle, nginx_proxy):
367326
"""
368327
Runs an nginx proxy, forcing partial downloads. HTTP requests are forwarded to port 8080
369328
(default port of the docker hawkBit instance). Returns the port the proxy is running on. This
370329
port can be set in the rauc-hawkbit-updater config to test partial downloads.
371330
"""
372-
location_options = {
373-
'limit_rate_after': '200k',
374-
'limit_rate': '70k',
375-
}
331+
with open(f'{os.path.dirname(os.path.abspath(__file__))}/nginx/partial.inc.in') as f:
332+
partial_include_template = Template(f.read())
333+
334+
partial_include_str = partial_include_template.substitute(
335+
rauc_bundle=rauc_bundle,
336+
)
337+
proxy_partial_config = tmp_path_factory.mktemp('nginx') / 'partial.inc'
338+
proxy_partial_config.write_text(partial_include_str)
339+
location_options = {'include': proxy_partial_config}
376340
return nginx_proxy(location_options)
377341

378342
@pytest.fixture

test/nginx/base.conf.in

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
load_module ${module_path}/ndk_http_module.so;
2+
load_module ${module_path}/ngx_http_lua_module.so;
3+
4+
daemon off;
5+
pid /tmp/hawkbit-nginx-${port}.pid;
6+
7+
# non-fatal alert for /var/log/nginx/error.log will still be shown
8+
# https://trac.nginx.org/nginx/ticket/147
9+
error_log stderr notice;
10+
11+
events { }
12+
13+
http {
14+
access_log /dev/null;
15+
16+
map $$ssl_client_s_dn $$ssl_client_s_dn_cn {
17+
default "";
18+
~CN=(?<CN>[^,]+) $$CN;
19+
}
20+
21+
server {
22+
listen 127.0.0.1:${port} ${ssl};
23+
listen [::1]:${port} ${ssl};
24+
25+
ssl_certificate pki/root-ca.crt;
26+
ssl_certificate_key pki/root-ca.key;
27+
ssl_client_certificate pki/root-ca.crt;
28+
${server_options}
29+
30+
location ~*/.*/controller/ {
31+
proxy_pass http://localhost:8080;
32+
33+
proxy_set_header Host $$http_host;
34+
proxy_set_header X-Real-IP $$remote_addr;
35+
proxy_set_header X-Forwarded-For $$proxy_add_x_forwarded_for;
36+
proxy_set_header X-Forwarded-Proto $$scheme;
37+
proxy_set_header X-Forwarded-Protocol $$scheme;
38+
proxy_set_header X-Forwarded-Port ${port};
39+
40+
proxy_set_header X-Ssl-Client-Cn $$ssl_client_s_dn_cn;
41+
42+
# These are required for clients to upload and download software.
43+
proxy_request_buffering off;
44+
client_max_body_size 1000m;
45+
46+
${location_options}
47+
}
48+
}
49+
}

test/nginx/partial.inc.in

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# serves only the first half of the dummy RAUC bundle by default, rest via
2+
# (pre-defined) range requests
3+
location ~*/.*/controller/v1/test-target/softwaremodules/.*/artifacts/ {
4+
content_by_lua_block {
5+
local file = io.open("${rauc_bundle}", "rb")
6+
local range = ngx.req.get_headers()["Range"]
7+
local size = file:seek("end")
8+
9+
if not range then
10+
-- send only first half
11+
file:seek("set", 0)
12+
ngx.print(file:read(size / 2))
13+
file:close()
14+
ngx.flush()
15+
ngx.exit(499)
16+
else
17+
local offset = range:match("bytes=(%d+)-")
18+
if not offset then return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) end
19+
offset = tonumber(offset)
20+
file:seek("set", offset)
21+
ngx.header["Content-Length"] = size - offset
22+
ngx.header["Content-Range"] = string.format("bytes %d-%d/%d", offset, size - 1, size)
23+
ngx.print(file:read(size - offset))
24+
file:close()
25+
end
26+
}
27+
}

0 commit comments

Comments
 (0)