Skip to content

Commit 27239d9

Browse files
committed
test: add Batch 2 mysql-* container tests and fix uptime=0 crashes
Add happy-path container integration tests for 11 mysql-* plugins (mysql-innodb-log-waits, mysql-joins, mysql-open-files, mysql-slow-queries, mysql-sorts, mysql-table-cache, mysql-table-definition-cache, mysql-table-locks, mysql-temp-tables, mysql-thread-cache, mysql-traffic). Each test iterates over the MARIADB_LTS_IMAGES matrix from lib.lftest, spanning MariaDB 10.6, 10.11, 11.4 and 11.8 across sclorg and upstream images, and uses the lib.lftest.run_mariadb() helper to hide the startup and .my.cnf boilerplate. mysql-table-cache pins the expected WARN state on a cold table cache because a freshly booted server legitimately trips the hit-rate alert and the test exists to keep that behavior in place. Fix two crashes exposed by the new tests: * mysql-joins: ZeroDivisionError in the joins-per-day calculation when `SHOW GLOBAL STATUS LIKE 'Uptime'` returns 0 in the first second after a server boot. * mysql-traffic: ZeroDivisionError in the qps calculation for the same reason, on top of the AttributeError crash caused by a missing `import lib.args` (the latter was already picked up by the ruff-format sweep in d60210d but appears here in the CHANGELOG because this commit pins it as intentional). Both plugins now clamp Uptime to a minimum of 1 second.
1 parent b41aca1 commit 27239d9

File tree

14 files changed

+860
-4
lines changed
  • check-plugins
    • mysql-innodb-log-waits/unit-test
    • mysql-joins
    • mysql-open-files/unit-test
    • mysql-slow-queries/unit-test
    • mysql-sorts/unit-test
    • mysql-table-cache/unit-test
    • mysql-table-definition-cache/unit-test
    • mysql-table-locks/unit-test
    • mysql-temp-tables/unit-test
    • mysql-thread-cache/unit-test
    • mysql-traffic

14 files changed

+860
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,10 @@ Monitoring Plugins:
150150
* keycloak-stats: fix incorrect symlink for lib
151151
* keycloak-version: detect the Keycloak version reliably even if the format of `/opt/keycloak/version.txt` changes, and fall back to the API when the file cannot be parsed ([#1070](https://github.com/Linuxfabrik/monitoring-plugins/issues/1070))
152152
* logfile: fix `OverflowError` when inode exceeds SQLite INTEGER range on Windows/NTFS ([#1035](https://github.com/Linuxfabrik/monitoring-plugins/issues/1035))
153+
* mysql-joins: fix `ZeroDivisionError` crash on a freshly booted server where `SHOW GLOBAL STATUS LIKE 'Uptime'` can return 0 in the first second after startup
153154
* mysql-memory: fix a crash in the "other process memory" calculation on hosts running psutil older than 5.3.0 ([#1070](https://github.com/Linuxfabrik/monitoring-plugins/issues/1070))
154155
* mysql-table-locks: fix the "X immediate / Y locks" summary - the "Y" value now correctly shows the total lock count (immediate + waited) instead of the immediate count twice ([#1070](https://github.com/Linuxfabrik/monitoring-plugins/issues/1070))
156+
* mysql-traffic: fix `AttributeError` crash caused by a missing `lib.args` import, and fix `ZeroDivisionError` crash on a freshly booted server where `SHOW GLOBAL STATUS LIKE 'Uptime'` can return 0 in the first second after startup
155157
* needs-restarting: the "Running Kernel X != Installed Kernel Y" line now shows up on Debian-based systems when `needrestart` reports a pending kernel upgrade
156158
* network-connections: the plugin now exits with the correct WARN/CRIT state when any threshold is violated; previously it always reported OK regardless of the loop's accumulated state ([#1070](https://github.com/Linuxfabrik/monitoring-plugins/issues/1070))
157159
* nextcloud-security-scan: only trigger a rescan for scans that are actually in the past; future-dated scans caused by clock skew no longer trigger an unnecessary rescan ([#1070](https://github.com/Linuxfabrik/monitoring-plugins/issues/1070))
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8; py-indent-offset: 4 -*-
3+
#
4+
# Author: Linuxfabrik GmbH, Zurich, Switzerland
5+
# Contact: info (at) linuxfabrik (dot) ch
6+
# https://www.linuxfabrik.ch/
7+
# License: The Unlicense, see LICENSE file.
8+
9+
# https://github.com/Linuxfabrik/monitoring-plugins/blob/main/CONTRIBUTING.md
10+
11+
"""Integration tests for the mysql-innodb-log-waits check plugin.
12+
13+
The plugin opens a real `pymysql` connection and reads
14+
`innodb_log_waits` / `innodb_log_writes` from `SHOW GLOBAL STATUS`,
15+
so a static fixture cannot drive it - we spin up an actual
16+
MariaDB server and let the plugin talk to it over TCP.
17+
18+
Per CONTRIBUTING's "Combine container tests with fixtures" rule,
19+
this file holds only the happy-path integration test. A freshly
20+
booted, idle MariaDB has zero log waits, which is STATE_OK with
21+
the plugin's defaults.
22+
23+
Runs against the current MariaDB LTS releases
24+
(`lib.lftest.MARIADB_LTS_IMAGES`).
25+
26+
Requirements:
27+
- podman (or docker) with a reachable socket
28+
- `pip install testcontainers`
29+
- `tools/run-unit-tests` auto-sets `CONTAINER_HOST` and
30+
`TESTCONTAINERS_RYUK_DISABLED`
31+
"""
32+
33+
import os
34+
import subprocess
35+
import sys
36+
import unittest
37+
38+
sys.path.insert(0, '..')
39+
40+
import lib.lftest
41+
from lib.globals import STATE_OK
42+
43+
IMAGES = lib.lftest.MARIADB_LTS_IMAGES
44+
45+
46+
class TestCheck(unittest.TestCase):
47+
pass
48+
49+
50+
def _check_image(test, image_pair):
51+
image, label = image_pair
52+
print(f'\n=== Testing {label} ({image}) ===', flush=True)
53+
with lib.lftest.run_mariadb(image) as (_container, defaults_file):
54+
cmd = [
55+
'python3', '../mysql-innodb-log-waits',
56+
f'--defaults-file={defaults_file}',
57+
]
58+
print(f'Run plugin: {" ".join(cmd)}', flush=True)
59+
result = subprocess.run(
60+
cmd,
61+
cwd=os.path.dirname(os.path.abspath(__file__)),
62+
capture_output=True,
63+
text=True,
64+
)
65+
combined = result.stdout + result.stderr
66+
print(f'Script output:\n{combined.strip()}', flush=True)
67+
68+
test.assertEqual(result.returncode, STATE_OK)
69+
test.assertIn("|'mysql_", combined)
70+
71+
72+
lib.lftest.attach_each(TestCheck, IMAGES, _check_image, id_func=lambda it: it[1])

check-plugins/mysql-joins/mysql-joins

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import lib.txt
2121
from lib.globals import STATE_OK, STATE_UNKNOWN, STATE_WARN
2222

2323
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
24-
__version__ = '2026040801'
24+
__version__ = '2026041301'
2525

2626
DESCRIPTION = """Checks the rate of joins executed without indexes in MySQL/MariaDB. A high rate of
2727
full joins indicates missing indexes and can severely impact query performance.
@@ -137,8 +137,13 @@ def main():
137137
mycalc['joins_without_indexes'] = int(mystat['Select_range_check']) + int(
138138
mystat['Select_full_join']
139139
)
140+
# Clamp Uptime to a minimum of 1 second so the per-day rate is
141+
# well-defined on a freshly booted server where
142+
# `SHOW GLOBAL STATUS LIKE 'Uptime'` can legitimately return 0 in
143+
# the first second after startup.
144+
uptime = max(int(mystat.get('Uptime') or 0), 1)
140145
mycalc['joins_without_indexes_per_day'] = round(
141-
mycalc['joins_without_indexes'] / (int(mystat['Uptime']) / 86400), 1
146+
mycalc['joins_without_indexes'] / (uptime / 86400), 1
142147
)
143148

144149
# Joins
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8; py-indent-offset: 4 -*-
3+
#
4+
# Author: Linuxfabrik GmbH, Zurich, Switzerland
5+
# Contact: info (at) linuxfabrik (dot) ch
6+
# https://www.linuxfabrik.ch/
7+
# License: The Unlicense, see LICENSE file.
8+
9+
# https://github.com/Linuxfabrik/monitoring-plugins/blob/main/CONTRIBUTING.md
10+
11+
"""Integration tests for the mysql-joins check plugin.
12+
13+
The plugin opens a real `pymysql` connection and reads join
14+
counters from `SHOW GLOBAL STATUS`, so a static fixture cannot
15+
drive it - we spin up an actual MariaDB server and let the plugin
16+
talk to it over TCP.
17+
18+
Per CONTRIBUTING's "Combine container tests with fixtures" rule,
19+
this file holds only the happy-path integration test. A freshly
20+
booted, idle MariaDB has near-zero join counters, which is
21+
STATE_OK with the plugin's defaults.
22+
23+
Runs against the current MariaDB LTS releases
24+
(`lib.lftest.MARIADB_LTS_IMAGES`).
25+
26+
Requirements:
27+
- podman (or docker) with a reachable socket
28+
- `pip install testcontainers`
29+
- `tools/run-unit-tests` auto-sets `CONTAINER_HOST` and
30+
`TESTCONTAINERS_RYUK_DISABLED`
31+
"""
32+
33+
import os
34+
import subprocess
35+
import sys
36+
import unittest
37+
38+
sys.path.insert(0, '..')
39+
40+
import lib.lftest
41+
from lib.globals import STATE_OK
42+
43+
IMAGES = lib.lftest.MARIADB_LTS_IMAGES
44+
45+
46+
class TestCheck(unittest.TestCase):
47+
pass
48+
49+
50+
def _check_image(test, image_pair):
51+
image, label = image_pair
52+
print(f'\n=== Testing {label} ({image}) ===', flush=True)
53+
with lib.lftest.run_mariadb(image) as (_container, defaults_file):
54+
cmd = [
55+
'python3', '../mysql-joins',
56+
f'--defaults-file={defaults_file}',
57+
]
58+
print(f'Run plugin: {" ".join(cmd)}', flush=True)
59+
result = subprocess.run(
60+
cmd,
61+
cwd=os.path.dirname(os.path.abspath(__file__)),
62+
capture_output=True,
63+
text=True,
64+
)
65+
combined = result.stdout + result.stderr
66+
print(f'Script output:\n{combined.strip()}', flush=True)
67+
68+
test.assertEqual(result.returncode, STATE_OK)
69+
test.assertIn("|'mysql_", combined)
70+
71+
72+
lib.lftest.attach_each(TestCheck, IMAGES, _check_image, id_func=lambda it: it[1])
73+
74+
75+
if __name__ == '__main__':
76+
unittest.main()
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8; py-indent-offset: 4 -*-
3+
#
4+
# Author: Linuxfabrik GmbH, Zurich, Switzerland
5+
# Contact: info (at) linuxfabrik (dot) ch
6+
# https://www.linuxfabrik.ch/
7+
# License: The Unlicense, see LICENSE file.
8+
9+
# https://github.com/Linuxfabrik/monitoring-plugins/blob/main/CONTRIBUTING.md
10+
11+
"""Integration tests for the mysql-open-files check plugin.
12+
13+
The plugin opens a real `pymysql` connection and reads
14+
`open_files` from `SHOW GLOBAL STATUS` against the
15+
`open_files_limit` variable, so a static fixture cannot drive it
16+
- we spin up an actual MariaDB server and let the plugin talk to
17+
it over TCP.
18+
19+
Per CONTRIBUTING's "Combine container tests with fixtures" rule,
20+
this file holds only the happy-path integration test. A freshly
21+
booted MariaDB holds only a few file descriptors against a
22+
`open_files_limit` of 1024+, which is STATE_OK with the plugin's
23+
defaults.
24+
25+
Runs against the current MariaDB LTS releases
26+
(`lib.lftest.MARIADB_LTS_IMAGES`).
27+
28+
Requirements:
29+
- podman (or docker) with a reachable socket
30+
- `pip install testcontainers`
31+
- `tools/run-unit-tests` auto-sets `CONTAINER_HOST` and
32+
`TESTCONTAINERS_RYUK_DISABLED`
33+
"""
34+
35+
import os
36+
import subprocess
37+
import sys
38+
import unittest
39+
40+
sys.path.insert(0, '..')
41+
42+
import lib.lftest
43+
from lib.globals import STATE_OK
44+
45+
IMAGES = lib.lftest.MARIADB_LTS_IMAGES
46+
47+
48+
class TestCheck(unittest.TestCase):
49+
pass
50+
51+
52+
def _check_image(test, image_pair):
53+
image, label = image_pair
54+
print(f'\n=== Testing {label} ({image}) ===', flush=True)
55+
with lib.lftest.run_mariadb(image) as (_container, defaults_file):
56+
cmd = [
57+
'python3', '../mysql-open-files',
58+
f'--defaults-file={defaults_file}',
59+
]
60+
print(f'Run plugin: {" ".join(cmd)}', flush=True)
61+
result = subprocess.run(
62+
cmd,
63+
cwd=os.path.dirname(os.path.abspath(__file__)),
64+
capture_output=True,
65+
text=True,
66+
)
67+
combined = result.stdout + result.stderr
68+
print(f'Script output:\n{combined.strip()}', flush=True)
69+
70+
test.assertEqual(result.returncode, STATE_OK)
71+
test.assertIn("|'mysql_", combined)
72+
73+
74+
lib.lftest.attach_each(TestCheck, IMAGES, _check_image, id_func=lambda it: it[1])
75+
76+
77+
if __name__ == '__main__':
78+
unittest.main()
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8; py-indent-offset: 4 -*-
3+
#
4+
# Author: Linuxfabrik GmbH, Zurich, Switzerland
5+
# Contact: info (at) linuxfabrik (dot) ch
6+
# https://www.linuxfabrik.ch/
7+
# License: The Unlicense, see LICENSE file.
8+
9+
# https://github.com/Linuxfabrik/monitoring-plugins/blob/main/CONTRIBUTING.md
10+
11+
"""Integration tests for the mysql-slow-queries check plugin.
12+
13+
The plugin opens a real `pymysql` connection and reads
14+
`slow_queries` / `questions` from `SHOW GLOBAL STATUS`, so a
15+
static fixture cannot drive it - we spin up an actual MariaDB
16+
server and let the plugin talk to it over TCP.
17+
18+
Per CONTRIBUTING's "Combine container tests with fixtures" rule,
19+
this file holds only the happy-path integration test. A freshly
20+
booted, idle MariaDB has zero slow queries, which is STATE_OK
21+
with the plugin's defaults.
22+
23+
Runs against the current MariaDB LTS releases
24+
(`lib.lftest.MARIADB_LTS_IMAGES`).
25+
26+
Requirements:
27+
- podman (or docker) with a reachable socket
28+
- `pip install testcontainers`
29+
- `tools/run-unit-tests` auto-sets `CONTAINER_HOST` and
30+
`TESTCONTAINERS_RYUK_DISABLED`
31+
"""
32+
33+
import os
34+
import subprocess
35+
import sys
36+
import unittest
37+
38+
sys.path.insert(0, '..')
39+
40+
import lib.lftest
41+
from lib.globals import STATE_OK
42+
43+
IMAGES = lib.lftest.MARIADB_LTS_IMAGES
44+
45+
46+
class TestCheck(unittest.TestCase):
47+
pass
48+
49+
50+
def _check_image(test, image_pair):
51+
image, label = image_pair
52+
print(f'\n=== Testing {label} ({image}) ===', flush=True)
53+
with lib.lftest.run_mariadb(image) as (_container, defaults_file):
54+
cmd = [
55+
'python3', '../mysql-slow-queries',
56+
f'--defaults-file={defaults_file}',
57+
]
58+
print(f'Run plugin: {" ".join(cmd)}', flush=True)
59+
result = subprocess.run(
60+
cmd,
61+
cwd=os.path.dirname(os.path.abspath(__file__)),
62+
capture_output=True,
63+
text=True,
64+
)
65+
combined = result.stdout + result.stderr
66+
print(f'Script output:\n{combined.strip()}', flush=True)
67+
68+
test.assertEqual(result.returncode, STATE_OK)
69+
test.assertIn("|'mysql_", combined)
70+
71+
72+
lib.lftest.attach_each(TestCheck, IMAGES, _check_image, id_func=lambda it: it[1])
73+
74+
75+
if __name__ == '__main__':
76+
unittest.main()

0 commit comments

Comments
 (0)