Skip to content

Commit d40eebf

Browse files
authored
Fix test_modules to add more time with better event waiting (#51312)
1 parent 52796f5 commit d40eebf

1 file changed

Lines changed: 52 additions & 11 deletions

File tree

filebeat/tests/system/test_modules.py

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,20 @@
55
import unittest
66
import glob
77
import subprocess
8+
import time
89

910
import json
1011
import logging
1112
from parameterized import parameterized
1213
from elasticsearch import Elasticsearch, NotFoundError
1314
from deepdiff import DeepDiff
1415

16+
# Maximum time (seconds) Filebeat is allowed to ingest a module's test file.
17+
# Inputs started with --once exit as soon as the file is fully read; tailing
18+
# inputs (journald, filestream) never exit, so the test stops Filebeat once the
19+
# expected events have been indexed or this timeout elapses.
20+
MODULE_INGEST_TIMEOUT = 60
21+
1522
# datasets for which @timestamp is removed due to date missing
1623
remove_timestamp = {
1724
"activemq.audit",
@@ -258,17 +265,19 @@ def run_on_file(self, module, fileset, test_file, cfgfile):
258265
stdout=output,
259266
stderr=subprocess.STDOUT,
260267
bufsize=0)
261-
# The journald input (used by some modules like 'system') does not
262-
# support the --once flag, hence we run Filebeat for at most
263-
# 15 seconds, if it does not finish, then kill the process.
264-
# If for any reason the Filebeat process gets stuck, only SIGKILL
265-
# will terminate it. We use SIGKILL to avoid leaking any running
266-
# process that could interfere with other tests
267-
try:
268-
proc.wait(15)
269-
except subprocess.TimeoutExpired:
270-
# Send SIGKILL
271-
proc.kill()
268+
if "--once" in cmd:
269+
# Process will exit on its own once the file is fully read. Wait the maximum time,
270+
# if it doesn't exit by then, kill it to avoid leaking a process.
271+
try:
272+
proc.wait(MODULE_INGEST_TIMEOUT)
273+
except subprocess.TimeoutExpired:
274+
proc.kill()
275+
proc.wait()
276+
else:
277+
# The journald and filestream inputs do not support --once and
278+
# tail the file forever. Stop filebeat once elasticsearch has
279+
# indexed the expected number of events (or the timeout elapses).
280+
self._wait_for_events_then_stop(proc, test_file, MODULE_INGEST_TIMEOUT)
272281

273282
# List of errors to check in filebeat output logs
274283
errors = ["error loading pipeline for fileset"]
@@ -311,6 +320,38 @@ def run_on_file(self, module, fileset, test_file, cfgfile):
311320

312321
self._test_expected_events(test_file, objects)
313322

323+
def _wait_for_events_then_stop(self, proc, test_file, timeout):
324+
expected = self._expected_event_count(test_file)
325+
deadline = time.monotonic() + timeout
326+
try:
327+
while time.monotonic() < deadline:
328+
if proc.poll() is not None:
329+
# Filebeat exited on its own; nothing left to stop.
330+
return
331+
if expected is not None and self._indexed_event_count() >= expected:
332+
break
333+
time.sleep(0.5)
334+
finally:
335+
if proc.poll() is None:
336+
proc.kill()
337+
proc.wait()
338+
339+
def _expected_event_count(self, test_file):
340+
if os.getenv("GENERATE"):
341+
return None
342+
try:
343+
with open(test_file + "-expected.json", "r") as f:
344+
return len(json.load(f))
345+
except (FileNotFoundError, ValueError):
346+
return None
347+
348+
def _indexed_event_count(self):
349+
try:
350+
self.es.indices.refresh(index=self.index_name)
351+
return self.es.count(index=self.index_name)["count"]
352+
except NotFoundError:
353+
return 0
354+
314355
def _test_expected_events(self, test_file, objects):
315356

316357
# Generate expected files if GENERATE env variable is set

0 commit comments

Comments
 (0)