Skip to content

Commit 033f5dc

Browse files
authored
checklogs: add count patterns option (#51)
* checklogs: count occurrences of each expr * checklogs: fix no cnt case
1 parent 418e2fb commit 033f5dc

File tree

2 files changed

+54
-6
lines changed

2 files changed

+54
-6
lines changed

docs/tasks.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ may contain the following nodes (as explained [here](https://docker-py.readthedo
110110
when a check fails, the task is considered failed; this node can either be a
111111
list of strings, meaning that all of them should be found, or a dictionary with
112112
the following nodes:
113-
* `all`: a list of strings that should be found in the logs
113+
* `all`: a list of strings that should be found in the logs or a list of
114+
dictionaries with two keys: `expr`, for the string, and `cnt`, to specify how many
115+
time it appears; you can skip `cnt`;
114116
* `none`: a list of strings that should not be found in the logs
115117
* `volumes`: either a list of volumes that should be mounted in the container, or a
116118
dictionary of volumes, each with the following nodes:
@@ -215,6 +217,22 @@ tasks:
215217
all: regex1 regex2 regex3 regex4 regex5
216218
```
217219

220+
If you want to control the number of each pattern:
221+
222+
```
223+
tasks:
224+
- name: MySQL
225+
type: mysql
226+
...
227+
checklogs:
228+
all:
229+
- expr: regex1
230+
cnt: 2
231+
- expr: regex2
232+
cnt: 1
233+
```
234+
235+
218236
Mounting an external volume in the container:
219237

220238
```

sipssert/task.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -336,18 +336,48 @@ def get_checklogs(self):
336336
return {"all": [], "none": []}
337337

338338
def check_logs(self):
339+
logs = self.container.logs().decode("UTF-8")
339340
self.log.info(self.checklogs)
340-
for regex in self.checklogs["all"]:
341+
for rule in self.checklogs["all"]:
341342
try:
342-
if not re.search(regex, self.container.logs().decode('UTF-8')):
343-
self.log.error(f"logs check failed for {regex}; should match")
344-
return False
343+
if isinstance(rule, dict):
344+
pattern = rule.get("expr", "")
345+
expected = rule.get("cnt", None)
346+
347+
if not pattern:
348+
self.log.error("logs check: empty pattern in rule")
349+
return False
350+
351+
matches = re.findall(pattern, logs)
352+
count = len(matches)
353+
354+
if expected is not None and count != expected:
355+
self.log.error(
356+
f"logs check failed for {pattern}; "
357+
f"expected {expected} matches, got {count}"
358+
)
359+
return False
360+
elif expected is None and count == 0:
361+
self.log.error(
362+
f"logs check failed for {pattern}; should match"
363+
)
364+
return False
365+
else:
366+
pattern = rule
367+
if not re.search(pattern, logs):
368+
self.log.error(
369+
f"logs check failed for {pattern}; should match"
370+
)
371+
return False
372+
345373
except Exception as e:
346374
self.log.error(f"error while checking logs: {e}")
347375
return False
376+
377+
348378
for regex in self.checklogs["none"]:
349379
try:
350-
if re.search(regex, self.container.logs().decode('UTF-8')):
380+
if re.search(regex, logs):
351381
self.log.error(f"logs check failed for {regex}; shouldn't match")
352382
return False
353383
except Exception as e:

0 commit comments

Comments
 (0)