Skip to content

Commit c8a27d4

Browse files
authored
fix: fix local sc4s and splunk setup for tests (#2935)
* fix: fix local sc4s and splunk setup for tests * fix: add timezone information to tests * fix: inacruate timestamp trimming * fix: add udp ports for 514 and 5614 * fix: use latest splunk and add additional waiting time to avoid flaky test for docker splunk
1 parent b7f2c76 commit c8a27d4

File tree

108 files changed

+511
-445
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+511
-445
lines changed

.github/workflows/ci-main.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ jobs:
3939
meta:
4040
runs-on: ubuntu-latest
4141
outputs:
42-
sc4s: ghcr.io/${{ github.repository }}/container3:${{ fromJSON(steps.docker_action_meta.outputs.json).labels['org.opencontainers.image.version'] }}
4342
container_tags: ${{ steps.docker_action_meta.outputs.tags }}
4443
container_labels: ${{ steps.docker_action_meta.outputs.labels }}
4544
container_buildtime: ${{ fromJSON(steps.docker_action_meta.outputs.json).labels['org.opencontainers.image.created'] }}

tests/conftest.py

Lines changed: 153 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,60 @@
33
# Use of this source code is governed by a BSD-2-clause-style
44
# license that can be found in the LICENSE-BSD2 file or at
55
# https://opensource.org/licenses/BSD-2-Clause
6+
import json
7+
import logging
68
import os
7-
import uuid
9+
from typing import Tuple
810
import shortuuid
911
from time import sleep
1012
import random
1113
import pytest
1214
import requests
1315
import splunklib.client as client
16+
import subprocess
17+
from filelock import FileLock
18+
19+
logger = logging.getLogger(__name__)
20+
21+
22+
def cleanup_docker_containers(keepalive=False):
23+
"""Cleanup Docker containers and volumes"""
24+
if keepalive:
25+
logger.info("Keepalive enabled, skipping Docker cleanup")
26+
return
27+
28+
logger.info("Cleaning up Docker containers...")
29+
try:
30+
# Get the project root directory (parent of tests directory)
31+
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
32+
compose_file = os.path.join(project_root, "tests", "docker-compose.yml")
33+
34+
result = subprocess.run(
35+
["docker", "compose", "-f", compose_file, "down", "-v"],
36+
cwd=project_root,
37+
check=False,
38+
capture_output=True,
39+
text=True,
40+
timeout=30
41+
)
42+
if result.returncode == 0:
43+
logger.info("Docker cleanup completed successfully")
44+
else:
45+
logger.warning(f"Docker cleanup finished with warnings: {result.stderr}")
46+
except subprocess.TimeoutExpired:
47+
logger.error("Docker cleanup timed out after 30 seconds")
48+
except Exception as e:
49+
logger.error(f"Failed to cleanup Docker containers: {e}")
50+
51+
52+
def pytest_sessionfinish(session, exitstatus):
53+
"""
54+
Cleanup hook that runs after all tests complete (success or failure).
55+
This ensures Docker containers are stopped even if tests fail or are interrupted.
56+
"""
57+
# Check if keepalive flag is set
58+
keepalive = session.config.getoption("--keepalive", default=False)
59+
cleanup_docker_containers(keepalive=keepalive)
1460

1561

1662
@pytest.fixture
@@ -82,7 +128,7 @@ def pytest_addoption(parser):
82128
"--splunk_hec_token",
83129
action="store",
84130
dest="splunk_hec_token",
85-
default=str(uuid.uuid1()),
131+
default="00000000-0000-0000-0000-0000000000000",
86132
help="Splunk HEC token",
87133
)
88134
group.addoption(
@@ -92,16 +138,14 @@ def pytest_addoption(parser):
92138
default="latest",
93139
help="Splunk version",
94140
)
95-
96-
97-
def is_responsive(url):
98-
try:
99-
response = requests.get(url)
100-
if response.status_code != 500:
101-
return True
102-
except ConnectionError:
103-
return False
104-
141+
group.addoption(
142+
"--splunk_docker_startup_wait",
143+
action="store",
144+
dest="splunk_docker_startup_wait",
145+
type=int,
146+
default=20,
147+
help="Additional wait time in seconds after Splunk Docker is responsive",
148+
)
105149

106150
def is_responsive_splunk(splunk):
107151
try:
@@ -113,9 +157,24 @@ def is_responsive_splunk(splunk):
113157
)
114158
return True
115159
except Exception:
160+
logger.warning("Splunk is unresponsive! Retrying...")
116161
return False
117162

118163

164+
def is_responsive_sc4s(host: str, port: int) -> bool:
165+
"""Check SC4S health endpoint"""
166+
try:
167+
response = requests.get(f"http://{host}:{port}/health", timeout=5)
168+
if response.status_code == 200:
169+
data = response.json()
170+
return data.get("status") == "healthy"
171+
except Exception as e:
172+
logger.debug(f"Health check failed: {e}")
173+
return False
174+
return False
175+
176+
177+
119178
@pytest.fixture(scope="session")
120179
def docker_compose_file(pytestconfig):
121180
"""Get an absolute path to the `docker-compose.yml` file. Override this
@@ -139,25 +198,16 @@ def splunk(request):
139198

140199
yield splunk
141200

142-
143201
@pytest.fixture(scope="session")
144-
def sc4s(request):
145-
if request.config.getoption("splunk_type") == "external":
146-
request.fixturenames.append("sc4s_external")
147-
sc4s = request.getfixturevalue("sc4s_external")
148-
elif request.config.getoption("splunk_type") == "docker":
149-
request.fixturenames.append("sc4s_docker")
150-
sc4s = request.getfixturevalue("sc4s_docker")
151-
else:
152-
raise ValueError
153-
154-
yield sc4s
155-
156-
157-
@pytest.fixture(scope="session")
158-
def splunk_docker(request, docker_services):
202+
def start_splunk_docker(request, docker_services):
159203
docker_services.start("splunk")
160-
port = docker_services.port_for("splunk", 8089)
204+
try:
205+
port = docker_services.port_for("splunk", 8089)
206+
logger.info(port)
207+
except Exception as e:
208+
raise RuntimeError(
209+
f"Docker service 'splunk' failed to start or is not running: {e}"
210+
) from e
161211

162212
splunk = {
163213
"host": docker_services.docker_ip,
@@ -169,9 +219,35 @@ def splunk_docker(request, docker_services):
169219
docker_services.wait_until_responsive(
170220
timeout=180.0, pause=1.0, check=lambda: is_responsive_splunk(splunk)
171221
)
222+
startup_wait = request.config.getoption("splunk_docker_startup_wait")
223+
if startup_wait > 0:
224+
logger.info(
225+
"Splunk reported healthy; waiting %s seconds for full initialization...",
226+
startup_wait,
227+
)
228+
sleep(startup_wait)
172229

173230
return splunk
174231

232+
@pytest.fixture(scope="session")
233+
def splunk_docker(request, worker_id, tmp_path_factory):
234+
if worker_id == "master":
235+
request.fixturenames.append("start_splunk_docker")
236+
splunk_docker = request.getfixturevalue("start_splunk_docker")
237+
return splunk_docker
238+
239+
root_tmp_dir = tmp_path_factory.getbasetemp().parent
240+
241+
fn = root_tmp_dir / "splunk_docker.json"
242+
with FileLock(str(fn) + ".lock"):
243+
if fn.is_file():
244+
splunk_docker = json.loads(fn.read_text())
245+
else:
246+
request.fixturenames.append("start_splunk_docker")
247+
splunk_docker = request.getfixturevalue("start_splunk_docker")
248+
fn.write_text(json.dumps(splunk_docker))
249+
250+
return splunk_docker
175251

176252
@pytest.fixture(scope="session")
177253
def splunk_external(request):
@@ -183,9 +259,8 @@ def splunk_external(request):
183259
}
184260
return splunk
185261

186-
187262
@pytest.fixture(scope="session")
188-
def sc4s_docker(docker_services):
263+
def start_sc4s_docker(docker_services, setup_splunk) -> Tuple[str, dict]:
189264
docker_services.start("sc4s")
190265

191266
ports = {
@@ -196,14 +271,48 @@ def sc4s_docker(docker_services):
196271
ports.update({5514: docker_services.port_for("sc4s", 5514)})
197272
ports.update({5601: docker_services.port_for("sc4s", 5601)})
198273
ports.update({6000: docker_services.port_for("sc4s", 6000)})
199-
# ports.update({6001: docker_services.port_for("sc4s", 6001)})
200274
ports.update({6002: docker_services.port_for("sc4s", 6002)})
275+
ports.update({8080: docker_services.port_for("sc4s", 8080)})
201276
ports.update({9000: docker_services.port_for("sc4s", 9000)})
202277
ports.update({9001: docker_services.port_for("sc4s", 9001)})
203278
ports.update({9002: docker_services.port_for("sc4s", 9002)})
204279

205-
return docker_services.docker_ip, ports
280+
docker_ip = docker_services.docker_ip
281+
health_port = ports[8080]
282+
283+
# Wait for SC4S health endpoint to report healthy status
284+
logger.info("Waiting for SC4S health endpoint to be responsive...")
285+
docker_services.wait_until_responsive(
286+
timeout=180.0,
287+
pause=2.0,
288+
check=lambda: is_responsive_sc4s(docker_ip, health_port)
289+
)
206290

291+
return docker_ip, ports
292+
293+
@pytest.fixture(scope="session")
294+
def sc4s_docker(request, worker_id, tmp_path_factory):
295+
if worker_id == "master":
296+
request.fixturenames.append("start_sc4s_docker")
297+
sc4s_docker = request.getfixturevalue("start_sc4s_docker")
298+
return sc4s_docker
299+
300+
root_tmp_dir = tmp_path_factory.getbasetemp().parent
301+
fn = root_tmp_dir / "sc4s_docker.json"
302+
303+
with FileLock(str(fn) + ".lock"):
304+
if fn.is_file():
305+
data = json.loads(fn.read_text())
306+
# this type conversion is requried because json keys are strings
307+
# and in almost all tests we are refrencing the port by int e.g setup_sc4s[1][514]
308+
sc4s_docker = (data[0], {int(k): v for k, v in data[1].items()})
309+
else:
310+
request.fixturenames.append("start_sc4s_docker")
311+
sc4s_docker = request.getfixturevalue("start_sc4s_docker")
312+
fn.write_text(json.dumps(sc4s_docker))
313+
314+
return sc4s_docker
315+
207316

208317
@pytest.fixture(scope="session")
209318
def sc4s_external(request):
@@ -222,11 +331,18 @@ def sc4s_external(request):
222331

223332
return request.config.getoption("sc4s_host"), ports
224333

334+
@pytest.fixture(scope="session")
335+
def setup_sc4s(request):
336+
if request.config.getoption("splunk_type") == "external":
337+
request.fixturenames.append("sc4s_external")
338+
sc4s = request.getfixturevalue("sc4s_external")
339+
elif request.config.getoption("splunk_type") == "docker":
340+
request.fixturenames.append("sc4s_docker")
341+
sc4s = request.getfixturevalue("sc4s_docker")
342+
else:
343+
raise ValueError
225344

226-
@pytest.fixture()
227-
def setup_sc4s(sc4s):
228-
return sc4s
229-
345+
yield sc4s
230346

231347
@pytest.fixture(scope="session")
232348
def setup_splunk(splunk):

0 commit comments

Comments
 (0)