Skip to content

Commit e1af93d

Browse files
committed
test(*-version): add --test hook and EOL-aware fixtures for 11 more version checkers
Convert the remaining shell-driven version-checker plugins to the fixture-based pattern established by redis-version, postgresql-version and mysql-version. Each plugin now has a `--test` hook that bypasses the live shell call and loads the version-string output from a stdout fixture, then runs the existing parser regex against it. The downstream `lib.version.check_eol()` HTTP call to endoflife.date still runs live, so the test result reflects today's actual EOL data. Plugin coverage and image sources (Red Hat family preferred where available, upstream/Debian fall-back when there is no RHEL-family equivalent that ships the version we want to exercise): - apache-httpd-version: 5 fixtures from registry.access.redhat.com/ubi{8,9,10}/httpd-24, fedora 43, debian 13. 6 testcases, 0 WARN (Apache 2.4 is still non-EOL). - apache-solr-version: 5 fixtures from docker.io/library/solr:{7,8, 9.4,9} - no Red Hat Solr container exists. Covers the EOL 7.x and 8.x cycles. 5 testcases, 2 WARN. - composer-version: 5 fixtures from docker.io/library/composer:{2.2, 2.7,2.8,2.9}. Composer ships as a single PHP file and the upstream image is canonical. 5 testcases, 2 WARN. - grafana-version: 5 fixtures from docker.io/grafana/grafana-oss 10.4 .. 12.4. No RHEL-family grafana container. 5 testcases, 3 WARN. - graylog-version: yum and dpkg listings captured from quay.io/rockylinux/rockylinux:9 and docker.io/library/debian:12 after fresh installs of graylog 5.2 / 6.3 / 7.0 repositories. 5 testcases, 1 WARN. - icinga-version: 6 fixtures from debian:{11,12,13} and ubuntu:{22.04,24.04}. The upstream `docker.io/icinga/icinga2` image's `(version: vX.Y.Z)` shape is intentionally rejected by the plugin, so debian-style `r<X>.<Y>.<Z>-1` outputs are needed. 6 testcases, 3 WARN. - mydumper-version: 3 fixtures from debian:11 and ubuntu:{22.04, 24.04}. This plugin uses the GitHub releases API instead of endoflife.date, so the test mode pins both the installed version and the upstream "latest" tag via the stdout/stderr slots of `lib.lftest.test()`. 3 testcases, 1 WARN. - openjdk-redhat-version: 4 stderr fixtures from registry.access.redhat.com/ubi8/openjdk-{8,11,17,21} (java prints to stderr) plus a stdout garbage edge case. Java 8 is pinned as UNKNOWN: the plugin transforms `1.8.0_482` to `1.8.0.482` and endoflife.date cannot map that to the upstream `8` cycle, that asymmetry is documented in the test docstring. 5 testcases, 1 WARN, 1 UNKNOWN. - openvpn-version: 5 fixtures from quay.io/rockylinux/rockylinux: {8,9,10} and fedora:43 after dnf install (with EPEL where required). 5 testcases, 2 WARN. - postfix-version: 5 fixtures from registry.access.redhat.com/ubi{8,9,10}/ubi and fedora:43 after fresh `dnf install postfix`. 5 testcases, 2 WARN. - valkey-version: 5 fixtures from docker.io/valkey/valkey:{7.2,8.0, 8.1,9.0}. No RHEL-family valkey container yet. 5 testcases, 0 WARN. Each plugin bumps `__version__` to `2026041301`, gains an `import lib.lftest`, and wires its existing version-extraction function through the `--test` argument the same way the three pilots already do. Every test docstring explicitly warns that the `assert-retc` and `assert-in` strings will need to be re-pinned every few months as the endoflife.date data drifts. The full unit-test suite is now at 132 plugins, all green under `tools/run-unit-tests --no-container`.
1 parent 7ae4b60 commit e1af93d

File tree

78 files changed

+1262
-86
lines changed

Some content is hidden

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

78 files changed

+1262
-86
lines changed

check-plugins/apache-httpd-version/apache-httpd-version

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ import sys
1616

1717
import lib.args
1818
import lib.base
19+
import lib.lftest
1920
import lib.shell
2021
import lib.version
2122
from lib.globals import STATE_UNKNOWN
2223

2324
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
24-
__version__ = '2026040801'
25+
__version__ = '2026041301'
2526

2627
DESCRIPTION = """Checks the installed Apache httpd version against the endoflife.date API and alerts if
2728
the version is end-of-life or if newer major, minor, or patch releases are available.
@@ -103,6 +104,13 @@ def parse_args():
103104
default=DEFAULT_OFFSET_EOL,
104105
)
105106

107+
parser.add_argument(
108+
'--test',
109+
help=lib.args.help('--test'),
110+
dest='TEST',
111+
type=lib.args.csv,
112+
)
113+
106114
parser.add_argument(
107115
'--timeout',
108116
help=lib.args.help('--timeout') + ' Default: %(default)s (seconds)',
@@ -115,13 +123,17 @@ def parse_args():
115123
return args
116124

117125

118-
def get_installed_version():
119-
success, result = lib.shell.shell_exec('httpd -v') # on RHEL
120-
if not success:
121-
success, result = lib.shell.shell_exec('apache2 -v') # on other systems
126+
def get_installed_version(test_arg=None):
127+
if test_arg is None:
128+
success, result = lib.shell.shell_exec('httpd -v') # on RHEL
122129
if not success:
123-
return ''
124-
stdout = result[0].strip()
130+
success, result = lib.shell.shell_exec('apache2 -v') # on other systems
131+
if not success:
132+
return ''
133+
stdout = result[0].strip()
134+
else:
135+
stdout, _, _ = lib.lftest.test(test_arg)
136+
stdout = stdout.strip()
125137
# where to find the version number in output?
126138
version_regex = r'/(\d+\.\d+\.\d+)'
127139
try:
@@ -141,7 +153,7 @@ def main():
141153
sys.exit(STATE_UNKNOWN)
142154

143155
# fetch data
144-
installed_version = get_installed_version()
156+
installed_version = get_installed_version(args.TEST)
145157
if not installed_version:
146158
lib.base.cu('Apache httpd not found.')
147159
state, msg = lib.version.check_eol(
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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+
"""Unit tests for the apache-httpd-version check plugin.
12+
13+
The fixtures in `stdout/` are the raw `httpd -v` / `apache2 -v`
14+
outputs captured from official Red Hat family httpd images
15+
(`registry.access.redhat.com/ubi{8,9,10}` plus
16+
`registry.fedoraproject.org/fedora:43`) and one Debian image
17+
(`docker.io/library/debian:13`) for the apache2 binary path. The
18+
plugin runs its `r'/(\\d+\\.\\d+\\.\\d+)'` regex against each one.
19+
20+
The endoflife.date `apache.json` feed currently lists 2.4 as the
21+
only non-EOL cycle, so all real captures resolve to STATE_OK with
22+
"EOL unknown" or a "patch ... available" hint. The
23+
`assert-retc` and `assert-in` strings will need to be re-pinned
24+
the day Apache 2.4 hits EOL or a 2.6 cycle appears.
25+
"""
26+
27+
import sys
28+
sys.path.insert(0, '..')
29+
30+
import unittest
31+
32+
from lib.globals import STATE_OK, STATE_UNKNOWN
33+
import lib.lftest
34+
35+
36+
TESTS = [
37+
{
38+
'id': 'ok-rhel8-httpd-2-4-37',
39+
'test': 'stdout/httpd-v-rhel8,,0',
40+
'assert-retc': STATE_OK,
41+
'assert-in': ['Apache httpd v2.4.37', 'EOL unknown'],
42+
},
43+
{
44+
'id': 'ok-rhel9-httpd-2-4-62',
45+
'test': 'stdout/httpd-v-rhel9,,0',
46+
'assert-retc': STATE_OK,
47+
'assert-in': ['Apache httpd v2.4.62', 'EOL unknown'],
48+
},
49+
{
50+
'id': 'ok-rhel10-httpd-2-4-63',
51+
'test': 'stdout/httpd-v-rhel10,,0',
52+
'assert-retc': STATE_OK,
53+
'assert-in': ['Apache httpd v2.4.63', 'EOL unknown'],
54+
},
55+
{
56+
'id': 'ok-fedora43-httpd-2-4-66',
57+
'test': 'stdout/httpd-v-fedora43,,0',
58+
'assert-retc': STATE_OK,
59+
'assert-in': ['Apache httpd v2.4.66', 'EOL unknown'],
60+
},
61+
{
62+
'id': 'ok-debian13-apache2-2-4-66',
63+
'test': 'stdout/apache2-v-debian13,,0',
64+
'assert-retc': STATE_OK,
65+
'assert-in': ['Apache httpd v2.4.66', 'EOL unknown'],
66+
},
67+
{
68+
'id': 'unknown-garbage-output',
69+
'test': 'stdout/garbage-output,,0',
70+
'assert-retc': STATE_UNKNOWN,
71+
'assert-in': ['Apache httpd not found.'],
72+
},
73+
]
74+
75+
76+
class TestCheck(unittest.TestCase):
77+
78+
check = '../apache-httpd-version'
79+
80+
def test(self):
81+
for t in TESTS:
82+
with self.subTest(id=t['id']):
83+
lib.lftest.run(self, self.check, t)
84+
85+
86+
if __name__ == '__main__':
87+
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Server version: Apache/2.4.66 (Debian)
2+
Server built: 2026-03-01T13:26:45
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
bash: httpd: command not found
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Server version: Apache/2.4.66 (Fedora Linux)
2+
Server built: Dec 9 2025 00:00:00
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Server version: Apache/2.4.63 (Red Hat Enterprise Linux)
2+
Server built: Dec 10 2025 00:00:00
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Server version: Apache/2.4.37 (Red Hat Enterprise Linux)
2+
Server built: Dec 12 2025 13:16:37
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Server version: Apache/2.4.62 (Red Hat Enterprise Linux)
2+
Server built: Dec 12 2025 00:00:00

check-plugins/apache-solr-version/apache-solr-version

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ import sys
1616

1717
import lib.args
1818
import lib.base
19+
import lib.lftest
1920
import lib.shell
2021
import lib.version
2122
from lib.globals import STATE_UNKNOWN
2223

2324
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
24-
__version__ = '2026040801'
25+
__version__ = '2026041301'
2526

2627
DESCRIPTION = """Checks the installed Apache Solr version against the endoflife.date API and alerts
2728
if the version is end-of-life or if newer major, minor, or patch releases are available.
@@ -111,6 +112,13 @@ def parse_args():
111112
default=DEFAULT_PATH,
112113
)
113114

115+
parser.add_argument(
116+
'--test',
117+
help=lib.args.help('--test'),
118+
dest='TEST',
119+
type=lib.args.csv,
120+
)
121+
114122
parser.add_argument(
115123
'--timeout',
116124
help=lib.args.help('--timeout') + ' Default: %(default)s (seconds)',
@@ -123,11 +131,15 @@ def parse_args():
123131
return args
124132

125133

126-
def get_installed_version(path):
127-
success, result = lib.shell.shell_exec(f'{path} version')
128-
if not success:
129-
return ''
130-
stdout = result[0].strip()
134+
def get_installed_version(path, test_arg=None):
135+
if test_arg is None:
136+
success, result = lib.shell.shell_exec(f'{path} version')
137+
if not success:
138+
return ''
139+
stdout = result[0].strip()
140+
else:
141+
stdout, _, _ = lib.lftest.test(test_arg)
142+
stdout = stdout.strip()
131143
# where to find the version number in output?
132144
# Solr version is: 9.4.0
133145
# 9.3.0
@@ -149,7 +161,7 @@ def main():
149161
sys.exit(STATE_UNKNOWN)
150162

151163
# fetch data
152-
installed_version = get_installed_version(args.PATH)
164+
installed_version = get_installed_version(args.PATH, args.TEST)
153165
if not installed_version:
154166
lib.base.cu(f'Apache Solr `{args.PATH}` not found.')
155167
state, msg = lib.version.check_eol(
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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+
"""Unit tests for the apache-solr-version check plugin.
12+
13+
The fixtures in `stdout/` are the raw `/opt/solr/bin/solr version`
14+
outputs captured from the official `docker.io/library/solr:<tag>`
15+
images on Docker Hub. There is no Red Hat family Solr container,
16+
so the upstream image is the right source. We deliberately
17+
include the EOL 7.x and 8.x cycles because production customers
18+
still run them. The plugin runs its `r'(\\d+\\.\\d+\\.\\d+)'`
19+
regex against each one. Note that 7.x and 8.x emit just the bare
20+
version string, while 9.x emits "Solr version is: X.Y.Z"
21+
preceded by a deprecation banner from 9.8 onwards.
22+
23+
Each testcase pins the expected Nagios state to whatever
24+
`lib.version.check_eol()` returned when these fixtures were saved
25+
against the live https://endoflife.date/api/solr.json data. The
26+
EOL table drifts over time (releases hit EOL, new majors appear),
27+
so the `assert-retc` and `assert-in` strings will need to be
28+
re-pinned every few months.
29+
"""
30+
31+
import sys
32+
sys.path.insert(0, '..')
33+
34+
import unittest
35+
36+
from lib.globals import STATE_OK, STATE_UNKNOWN, STATE_WARN
37+
import lib.lftest
38+
39+
40+
TESTS = [
41+
{
42+
'id': 'warn-solr-7-7-eol',
43+
'test': 'stdout/solr-version-7,,0',
44+
'assert-retc': STATE_WARN,
45+
'assert-in': ['Apache Solr v7.7.3', 'EOL 2022-05-11', '[WARNING]'],
46+
},
47+
{
48+
'id': 'warn-solr-8-11-eol',
49+
'test': 'stdout/solr-version-8,,0',
50+
'assert-retc': STATE_WARN,
51+
'assert-in': ['Apache Solr v8.11.4', 'EOL 2024-10-25', '[WARNING]'],
52+
},
53+
{
54+
'id': 'ok-solr-9-4',
55+
'test': 'stdout/solr-version-9.4,,0',
56+
'assert-retc': STATE_OK,
57+
'assert-in': ['Apache Solr v9.4.1', 'EOL unknown'],
58+
},
59+
{
60+
'id': 'ok-solr-9-10',
61+
'test': 'stdout/solr-version-9.10,,0',
62+
'assert-retc': STATE_OK,
63+
'assert-in': ['Apache Solr v9.10.1', 'EOL unknown'],
64+
},
65+
{
66+
'id': 'unknown-garbage-output',
67+
'test': 'stdout/garbage-output,,0',
68+
'assert-retc': STATE_UNKNOWN,
69+
'assert-in': ['Apache Solr', 'not found.'],
70+
},
71+
]
72+
73+
74+
class TestCheck(unittest.TestCase):
75+
76+
check = '../apache-solr-version'
77+
78+
def test(self):
79+
for t in TESTS:
80+
with self.subTest(id=t['id']):
81+
lib.lftest.run(self, self.check, t)
82+
83+
84+
if __name__ == '__main__':
85+
unittest.main()

0 commit comments

Comments
 (0)