Skip to content

Commit 6e42a70

Browse files
committed
test: add Batch 4 mysql-* container tests (binlog-cache, database-metrics, table-indexes, user-security)
Four happy-path (and intentional-warn-path) container tests for the mysql-* plugins that need explicit schema, config or privilege setup beyond a stock MariaDB boot. - mysql-binlog-cache: boots MariaDB with `--log-bin --server-id=1` so the binlog counters are actually exposed and exercises the "binlog active" code path. - mysql-database-metrics: seeds a small user table in the default `test` database via the run_mariadb seed= param so information_schema has a real user schema to walk; asserts the happy-path "Everything is ok." output. - mysql-table-indexes: seeds one indexed and one intentionally unindexed table, then pins STATE_WARN plus the "no_index_tbl" string. A freshly booted default MariaDB with a table missing an index is the correct monitoring signal and the test exists to keep the detection logic in place. - mysql-user-security: grants the test user SELECT on mysql.* so the plugin can read `mysql.user` / `mysql.global_priv`, then pins the two legitimate warnings every default MariaDB image produces (test user has the username as password; both root and test are declared with host `%`). The grant step works across both image families: sclorg boots MariaDB as the mysql OS user which lets `mariadb -uroot` connect without a password via socket auth, while the upstream image requires the `-p<root-password>` flag. The test tries the password-less variant first and falls back on failure. mysql-logfile was considered for this batch but deferred: it reads a local filesystem error log from the host running the plugin, which is a cpu-usage-style "plugin runs inside the container" pattern rather than a "plugin runs from the host against a service container" pattern.
1 parent 1606478 commit 6e42a70

File tree

4 files changed

+362
-0
lines changed
  • check-plugins
    • mysql-binlog-cache/unit-test
    • mysql-database-metrics/unit-test
    • mysql-table-indexes/unit-test
    • mysql-user-security/unit-test

4 files changed

+362
-0
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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-binlog-cache check plugin.
12+
13+
The plugin opens a real `pymysql` connection and reads
14+
`Binlog_cache_use` / `Binlog_cache_disk_use` from
15+
`SHOW GLOBAL STATUS` plus `binlog_cache_size` from `SHOW VARIABLES`,
16+
so a static fixture cannot drive it. A freshly booted MariaDB with
17+
the binary log disabled has all binlog counters at zero and the
18+
variables are still exposed, so the plugin reports OK. To make
19+
sure the "binlog actually active" code path is exercised too, we
20+
boot the server with `--log-bin` enabled.
21+
22+
Per CONTRIBUTING's "Combine container tests with fixtures" rule,
23+
this file holds only the happy-path integration test.
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+
46+
IMAGES = lib.lftest.MARIADB_LTS_IMAGES
47+
48+
49+
class TestCheck(unittest.TestCase):
50+
pass
51+
52+
53+
def _check_image(test, image_pair):
54+
image, label = image_pair
55+
print(f'\n=== Testing {label} ({image}) ===', flush=True)
56+
with lib.lftest.run_mariadb(
57+
image,
58+
extra_args='--log-bin --server-id=1',
59+
) as (_container, defaults_file):
60+
cmd = [
61+
'python3', '../mysql-binlog-cache',
62+
f'--defaults-file={defaults_file}',
63+
]
64+
print(f'Run plugin: {" ".join(cmd)}', flush=True)
65+
result = subprocess.run(
66+
cmd,
67+
cwd=os.path.dirname(os.path.abspath(__file__)),
68+
capture_output=True,
69+
text=True,
70+
)
71+
combined = result.stdout + result.stderr
72+
print(f'Script output:\n{combined.strip()}', flush=True)
73+
74+
test.assertEqual(result.returncode, STATE_OK)
75+
test.assertIn("|'mysql_", combined)
76+
77+
78+
lib.lftest.attach_each(TestCheck, IMAGES, _check_image, id_func=lambda it: it[1])
79+
80+
81+
if __name__ == '__main__':
82+
unittest.main()
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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-database-metrics check plugin.
12+
13+
The plugin opens a real `pymysql` connection and queries
14+
`information_schema.schemata`, then for every user database it
15+
walks `information_schema.tables` / `COLUMNS` to collect table
16+
count, row count, data/index size and charset information. We
17+
spin up an actual MariaDB server, seed the default `test`
18+
database with a tiny table so the plugin has something to walk,
19+
and let it talk to the server over TCP.
20+
21+
Per CONTRIBUTING's "Combine container tests with fixtures" rule,
22+
this file holds only the happy-path integration test.
23+
24+
Runs against the current MariaDB LTS releases
25+
(`lib.lftest.MARIADB_LTS_IMAGES`).
26+
27+
Requirements:
28+
- podman (or docker) with a reachable socket
29+
- `pip install testcontainers`
30+
- `tools/run-unit-tests` auto-sets `CONTAINER_HOST` and
31+
`TESTCONTAINERS_RYUK_DISABLED`
32+
"""
33+
34+
import os
35+
import subprocess
36+
import sys
37+
import unittest
38+
39+
sys.path.insert(0, '..')
40+
41+
import lib.lftest
42+
from lib.globals import STATE_OK
43+
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(
56+
image,
57+
seed=(
58+
'CREATE TABLE countries ('
59+
'code CHAR(3) PRIMARY KEY, '
60+
'name VARCHAR(64) NOT NULL'
61+
') ENGINE=InnoDB; '
62+
"INSERT INTO countries VALUES "
63+
"('CHE', 'Switzerland'), ('DEU', 'Germany'), ('FRA', 'France')"
64+
),
65+
) as (_container, defaults_file):
66+
cmd = [
67+
'python3', '../mysql-database-metrics',
68+
f'--defaults-file={defaults_file}',
69+
]
70+
print(f'Run plugin: {" ".join(cmd)}', flush=True)
71+
result = subprocess.run(
72+
cmd,
73+
cwd=os.path.dirname(os.path.abspath(__file__)),
74+
capture_output=True,
75+
text=True,
76+
)
77+
combined = result.stdout + result.stderr
78+
print(f'Script output:\n{combined.strip()}', flush=True)
79+
80+
test.assertEqual(result.returncode, STATE_OK)
81+
test.assertIn('Everything is ok', combined)
82+
83+
84+
lib.lftest.attach_each(TestCheck, IMAGES, _check_image, id_func=lambda it: it[1])
85+
86+
87+
if __name__ == '__main__':
88+
unittest.main()
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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-table-indexes check plugin.
12+
13+
The plugin opens a real `pymysql` connection and walks
14+
`information_schema.tables` / `STATISTICS` for every user
15+
database to flag tables that have no index at all. We spin up an
16+
actual MariaDB server and seed the default `test` database with
17+
two tables - one fully indexed (PK + secondary), one intentionally
18+
without any index - so the plugin has real schema to walk.
19+
20+
Per CONTRIBUTING's "Combine container tests with fixtures" rule,
21+
this file holds only the happy-path integration test. A freshly
22+
booted MariaDB with one indexed and one unindexed table trips the
23+
"table without index" warning, which is the correct monitoring
24+
signal on a real server and the behavior the test pins.
25+
26+
Runs against the current MariaDB LTS releases
27+
(`lib.lftest.MARIADB_LTS_IMAGES`).
28+
29+
Requirements:
30+
- podman (or docker) with a reachable socket
31+
- `pip install testcontainers`
32+
- `tools/run-unit-tests` auto-sets `CONTAINER_HOST` and
33+
`TESTCONTAINERS_RYUK_DISABLED`
34+
"""
35+
36+
import os
37+
import subprocess
38+
import sys
39+
import unittest
40+
41+
sys.path.insert(0, '..')
42+
43+
import lib.lftest
44+
from lib.globals import STATE_WARN
45+
46+
47+
IMAGES = lib.lftest.MARIADB_LTS_IMAGES
48+
49+
50+
class TestCheck(unittest.TestCase):
51+
pass
52+
53+
54+
def _check_image(test, image_pair):
55+
image, label = image_pair
56+
print(f'\n=== Testing {label} ({image}) ===', flush=True)
57+
with lib.lftest.run_mariadb(
58+
image,
59+
seed=(
60+
'CREATE TABLE indexed_tbl ('
61+
'id INT PRIMARY KEY, '
62+
'name VARCHAR(64), '
63+
'INDEX idx_name (name)'
64+
') ENGINE=InnoDB; '
65+
'CREATE TABLE no_index_tbl ('
66+
'col1 VARCHAR(16), '
67+
'col2 VARCHAR(16)'
68+
') ENGINE=InnoDB'
69+
),
70+
) as (_container, defaults_file):
71+
cmd = [
72+
'python3', '../mysql-table-indexes',
73+
f'--defaults-file={defaults_file}',
74+
]
75+
print(f'Run plugin: {" ".join(cmd)}', flush=True)
76+
result = subprocess.run(
77+
cmd,
78+
cwd=os.path.dirname(os.path.abspath(__file__)),
79+
capture_output=True,
80+
text=True,
81+
)
82+
combined = result.stdout + result.stderr
83+
print(f'Script output:\n{combined.strip()}', flush=True)
84+
85+
test.assertEqual(result.returncode, STATE_WARN)
86+
test.assertIn('no_index_tbl', combined)
87+
88+
89+
lib.lftest.attach_each(TestCheck, IMAGES, _check_image, id_func=lambda it: it[1])
90+
91+
92+
if __name__ == '__main__':
93+
unittest.main()
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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-user-security check plugin.
12+
13+
The plugin opens a real `pymysql` connection and queries
14+
`mysql.user` / `mysql.global_priv` for anonymous accounts,
15+
passwordless accounts and hosts granted wildcard access. Reading
16+
the `mysql` system database requires elevated privileges, so the
17+
test grants the regular `test` user SELECT on `mysql.*` via
18+
`container.exec` as root before running the plugin.
19+
20+
On a default-configured MariaDB image, the plugin legitimately
21+
flags two security issues: the `test` user has its username as
22+
its password ("user with username as password"), and both root
23+
and test are defined as `@'%'` ("accounts without hostname
24+
restriction"). Both are real findings worth monitoring; the test
25+
pins the WARN state so the plugin's detection logic stays in
26+
place and a future refactor cannot silently mask either signal.
27+
28+
Runs against the current MariaDB LTS releases
29+
(`lib.lftest.MARIADB_LTS_IMAGES`).
30+
31+
Requirements:
32+
- podman (or docker) with a reachable socket
33+
- `pip install testcontainers`
34+
- `tools/run-unit-tests` auto-sets `CONTAINER_HOST` and
35+
`TESTCONTAINERS_RYUK_DISABLED`
36+
"""
37+
38+
import os
39+
import subprocess
40+
import sys
41+
import unittest
42+
43+
sys.path.insert(0, '..')
44+
45+
import lib.lftest
46+
from lib.globals import STATE_WARN
47+
48+
49+
IMAGES = lib.lftest.MARIADB_LTS_IMAGES
50+
51+
52+
class TestCheck(unittest.TestCase):
53+
pass
54+
55+
56+
def _check_image(test, image_pair):
57+
image, label = image_pair
58+
print(f'\n=== Testing {label} ({image}) ===', flush=True)
59+
with lib.lftest.run_mariadb(image) as (container, defaults_file):
60+
# Grant the `test` user read access to the `mysql` system
61+
# database so it can query `mysql.user` / `mysql.global_priv`.
62+
# sclorg images accept root via socket auth (no password
63+
# because the container runs as the mysql OS user); upstream
64+
# images require the `-p` password. Try without password
65+
# first, fall back to the password-auth variant.
66+
container.exec([
67+
'sh', '-c',
68+
'if command -v mariadb >/dev/null 2>&1; then C=mariadb; '
69+
'else C=mysql; fi; '
70+
'GRANT_SQL="GRANT SELECT ON mysql.* TO '
71+
"'test'@'%'"
72+
'"; '
73+
'"$C" -uroot -e "$GRANT_SQL" 2>/dev/null '
74+
'|| "$C" -uroot -ptest -e "$GRANT_SQL"',
75+
])
76+
77+
cmd = [
78+
'python3', '../mysql-user-security',
79+
f'--defaults-file={defaults_file}',
80+
]
81+
print(f'Run plugin: {" ".join(cmd)}', flush=True)
82+
result = subprocess.run(
83+
cmd,
84+
cwd=os.path.dirname(os.path.abspath(__file__)),
85+
capture_output=True,
86+
text=True,
87+
)
88+
combined = result.stdout + result.stderr
89+
print(f'Script output:\n{combined.strip()}', flush=True)
90+
91+
test.assertEqual(result.returncode, STATE_WARN)
92+
test.assertIn('without hostname restriction', combined)
93+
94+
95+
lib.lftest.attach_each(TestCheck, IMAGES, _check_image, id_func=lambda it: it[1])
96+
97+
98+
if __name__ == '__main__':
99+
unittest.main()

0 commit comments

Comments
 (0)