Skip to content

Commit d367f7c

Browse files
committed
tests: add integration tests for configuration hot reloading
1 parent fb8d88a commit d367f7c

File tree

3 files changed

+108
-17
lines changed

3 files changed

+108
-17
lines changed

tests/conftest.py

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
from concurrent import futures
21
import os
32
from shutil import rmtree
4-
from tempfile import mkdtemp
3+
from tempfile import NamedTemporaryFile, mkdtemp
54
from time import sleep
65

76
import docker
87
import pytest
98
import requests
9+
import yaml
1010

1111
from server import FileActivityService
1212

@@ -81,21 +81,38 @@ def dump_logs(container, file):
8181

8282

8383
@pytest.fixture
84-
def fact(request, docker_client, monitored_dir, server, logs_dir):
84+
def fact_config(request, monitored_dir, logs_dir):
85+
cwd = os.getcwd()
86+
config = {
87+
'paths': [monitored_dir],
88+
'url': 'http://127.0.0.1:9999',
89+
'endpoint': {
90+
'address': '127.0.0.1:9000',
91+
'expose_metrics': True,
92+
'health_check': True,
93+
},
94+
'json': True,
95+
}
96+
config_file = NamedTemporaryFile(
97+
prefix='fact-config-', suffix='.yml', dir=cwd, mode='w')
98+
yaml.dump(config, config_file)
99+
100+
yield config, config_file.name
101+
with open(os.path.join(logs_dir, 'fact.yml'), 'w') as f:
102+
with open(config_file.name, 'r') as r:
103+
f.write(r.read())
104+
config_file.close()
105+
106+
107+
@pytest.fixture
108+
def fact(request, docker_client, fact_config, server, logs_dir):
85109
"""
86110
Run the fact docker container for integration tests.
87111
"""
88-
command = [
89-
'http://127.0.0.1:9999',
90-
'-p', monitored_dir,
91-
'--expose-metrics',
92-
'--health-check',
93-
'--json',
94-
]
112+
config, config_file = fact_config
95113
image = request.config.getoption('--image')
96114
container = docker_client.containers.run(
97115
image,
98-
command=command,
99116
detach=True,
100117
environment={
101118
'FACT_LOGLEVEL': 'debug',
@@ -121,14 +138,19 @@ def fact(request, docker_client, monitored_dir, server, logs_dir):
121138
'bind': '/host/usr/lib/os-release',
122139
'mode': 'ro',
123140
},
141+
config_file: {
142+
'bind': '/etc/stackrox/fact.yml',
143+
'mode': 'ro',
144+
}
124145
},
125146
)
126147

127148
container_log = os.path.join(logs_dir, 'fact.log')
128149
# Wait for container to be ready
129150
for _ in range(3):
130151
try:
131-
resp = requests.get('http://127.0.0.1:9000/health_check')
152+
resp = requests.get(
153+
f'http://{config["endpoint"]["address"]}/health_check')
132154
if resp.status_code == 200:
133155
break
134156
except (requests.RequestException, requests.ConnectionError) as e:
@@ -143,11 +165,13 @@ def fact(request, docker_client, monitored_dir, server, logs_dir):
143165
yield container
144166

145167
# Capture prometheus metrics before stopping the container
146-
metric_log = os.path.join(logs_dir, 'metrics')
147-
resp = requests.get('http://127.0.0.1:9000/metrics')
148-
if resp.status_code == 200:
149-
with open(metric_log, 'w') as f:
150-
f.write(resp.text)
168+
if config['endpoint']['expose_metrics']:
169+
metric_log = os.path.join(logs_dir, 'metrics')
170+
resp = requests.get(
171+
f'http://{config["endpoint"]["address"]}/metrics')
172+
if resp.status_code == 200:
173+
with open(metric_log, 'w') as f:
174+
f.write(resp.text)
151175

152176
container.stop(timeout=1)
153177
exit_status = container.wait(timeout=1)

tests/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ grpcio==1.73.1
33
grpcio-tools==1.73.1
44
pytest==8.4.1
55
requests==2.32.4
6+
pyyaml==6.0.3

tests/test_config_hotreload.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from time import sleep
2+
import pytest
3+
import requests
4+
import yaml
5+
6+
DEFAULT_URL = 'http://127.0.0.1:9000'
7+
8+
9+
def assert_endpoint(endpoint, status_code=200):
10+
resp = requests.get(f'{DEFAULT_URL}/{endpoint}')
11+
assert resp.status_code == status_code
12+
13+
14+
def reload_config(fact, config, file):
15+
with open(file, 'w') as f:
16+
yaml.dump(config, f)
17+
fact.kill('SIGHUP')
18+
sleep(0.1)
19+
20+
21+
cases = [('metrics', 'expose_metrics'), ('health_check', 'health_check')]
22+
23+
24+
@pytest.mark.parametrize('case', cases, ids=['metrics', 'health_check'])
25+
def test_endpoint(fact, fact_config, case):
26+
"""
27+
Test the endpoints configurability
28+
"""
29+
endpoint, field = case
30+
31+
# Endpoints are assumed to start up enabled.
32+
assert_endpoint(endpoint)
33+
34+
# Mark the endpoint as off and reload configuration
35+
config, config_file = fact_config
36+
config['endpoint'][field] = False
37+
reload_config(fact, config, config_file)
38+
39+
assert_endpoint(endpoint, 503)
40+
41+
42+
def test_endpoint_disable_all(fact, fact_config):
43+
"""
44+
Disable all endpoints and check the default port is not bound
45+
"""
46+
config, config_file = fact_config
47+
config['endpoint'] = {
48+
'health_check': False,
49+
'expose_metrics': False,
50+
}
51+
reload_config(fact, config, config_file)
52+
53+
with pytest.raises(requests.ConnectionError):
54+
requests.get(f'{DEFAULT_URL}/metrics')
55+
56+
57+
def test_endpoint_address_change(fact, fact_config):
58+
config, config_file = fact_config
59+
config['endpoint']['address'] = '127.0.0.1:9001'
60+
reload_config(fact, config, config_file)
61+
62+
with pytest.raises(requests.ConnectionError):
63+
requests.get(f'{DEFAULT_URL}/metrics')
64+
65+
resp = requests.get('http://127.0.0.1:9001/metrics')
66+
assert resp.status_code == 200

0 commit comments

Comments
 (0)