Skip to content

Commit c23ebac

Browse files
authored
Merge pull request #63 from drewkerrigan/v2-1
Release 2.1
2 parents 4c89a8a + a014944 commit c23ebac

7 files changed

+173
-32
lines changed

Diff for: .pylintrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# pylint config
22
[MESSAGES CONTROL]
3-
disable=line-too-long, redefined-outer-name, too-many-arguments, too-many-instance-attributes, fixme, invalid-name, superfluous-parens, missing-function-docstring, missing-module-docstring, multiple-imports, no-else-return, too-many-return-statements
3+
disable=line-too-long, redefined-outer-name, too-many-arguments, too-many-instance-attributes, fixme, invalid-name, superfluous-parens, missing-function-docstring, missing-module-docstring, multiple-imports, no-else-return, too-many-return-statements, too-many-branches, too-many-statements
44
[MASTER]
55
ignore-patterns=^test.*

Diff for: check_http_json.py

+21-8
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,9 @@ def __init__(self, json_data, rules_args):
225225
if self.rules.value_separator:
226226
value_separator = self.rules.value_separator
227227
self.helper = JsonHelper(self.data, separator, value_separator)
228-
debugPrint(rules_args.debug, "rules:%s" % rules_args)
229-
debugPrint(rules_args.debug, "separator:%s" % separator)
230-
debugPrint(rules_args.debug, "value_separator:%s" % value_separator)
228+
debugPrint(rules_args.debug, "rules: %s" % rules_args)
229+
debugPrint(rules_args.debug, "separator: %s" % separator)
230+
debugPrint(rules_args.debug, "value_separator: %s" % value_separator)
231231
self.metric_list = self.expandKeys(self.rules.metric_list)
232232
self.key_threshold_warning = self.expandKeys(
233233
self.rules.key_threshold_warning)
@@ -346,6 +346,8 @@ def checkWarning(self):
346346

347347
def checkCritical(self):
348348
failure = ''
349+
if not self.data:
350+
failure = " Empty JSON data."
349351
if self.key_threshold_critical is not None:
350352
failure += self.checkThresholds(self.key_threshold_critical)
351353
if self.key_value_list_critical is not None:
@@ -424,7 +426,7 @@ def parseArgs(args):
424426
parser.add_argument('-s', '--ssl', action='store_true',
425427
help='use TLS to connect to remote host')
426428
parser.add_argument('-H', '--host', dest='host',
427-
required=not ('-V' in sys.argv or '--version' in sys.argv),
429+
required=not ('-V' in args or '--version' in args),
428430
help='remote host to query')
429431
parser.add_argument('-k', '--insecure', action='store_true',
430432
help='do not check server SSL certificate')
@@ -524,10 +526,12 @@ def debugPrint(debug_flag, message, pretty_flag=False):
524526
print(message)
525527

526528

527-
# Program entry point
528-
if __name__ == "__main__":
529+
def main(cliargs):
530+
"""
531+
Main entrypoint for CLI
532+
"""
529533

530-
args = parseArgs(sys.argv[1:])
534+
args = parseArgs(cliargs)
531535
nagios = NagiosHelper()
532536
context = None
533537

@@ -607,7 +611,11 @@ def debugPrint(debug_flag, message, pretty_flag=False):
607611
json_data = response.read()
608612

609613
except HTTPError as e:
610-
nagios.append_unknown(" HTTPError[%s], url:%s" % (str(e.code), url))
614+
# Try to recover from HTTP Error, if there is JSON in the response
615+
if "json" in e.info().get_content_subtype():
616+
json_data = e.read()
617+
else:
618+
nagios.append_unknown(" HTTPError[%s], url:%s" % (str(e.code), url))
611619
except URLError as e:
612620
nagios.append_critical(" URLError[%s], url:%s" % (str(e.reason), url))
613621

@@ -630,4 +638,9 @@ def debugPrint(debug_flag, message, pretty_flag=False):
630638
print(nagios.getMessage())
631639
sys.exit(nagios.getCode())
632640

641+
642+
if __name__ == "__main__":
643+
# Program entry point
644+
main(sys.argv[1:])
645+
633646
#EOF

Diff for: makefile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.PHONY: lint test coverage
2+
3+
lint:
4+
python3 -m pylint check_http_json.py
5+
test:
6+
python3 -m unittest discover
7+
coverage:
8+
python3 -m coverage run -m unittest discover
9+
python3 -m coverage report -m --include check_http_json.py

Diff for: test/requirements.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
coverage==5.0.3
2+
pylint==2.4.4

Diff for: test/test_check_http_json.py

+21-1
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ def test_array_with_missing_element(self):
256256

257257
# This should not throw a KeyError
258258
data = '{}'
259-
self.check_data(rules.dash_q(['(0).Node,foobar', '(1).Node,missing']), data, WARNING_CODE)
259+
self.check_data(rules.dash_q(['(0).Node,foobar', '(1).Node,missing']), data, CRITICAL_CODE)
260260

261261
def test_subelem(self):
262262

@@ -274,3 +274,23 @@ def test_subarrayelem_missing_elem(self):
274274
self.check_data(rules.dash_E(['(*).capacity.value.too_deep']), data, CRITICAL_CODE)
275275
# Should not throw keyerror
276276
self.check_data(rules.dash_E(['foo']), data, CRITICAL_CODE)
277+
278+
279+
def test_empty_key_value_array(self):
280+
"""
281+
https://github.com/drewkerrigan/nagios-http-json/issues/61
282+
"""
283+
284+
rules = RulesHelper()
285+
286+
# This should simply work
287+
data = '[{"update_status": "finished"},{"update_status": "finished"}]'
288+
self.check_data(rules.dash_q(['(*).update_status,finished']), data, OK_CODE)
289+
290+
# This should warn us
291+
data = '[{"update_status": "finished"},{"update_status": "failure"}]'
292+
self.check_data(rules.dash_q(['(*).update_status,finished']), data, WARNING_CODE)
293+
294+
# This should throw an error
295+
data = '[]'
296+
self.check_data(rules.dash_q(['(*).update_status,warn_me']), data, CRITICAL_CODE)

Diff for: test/test_cli.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/usr/bin/env python3
2+
3+
4+
import unittest
5+
import unittest.mock as mock
6+
import sys
7+
import os
8+
9+
sys.path.append('..')
10+
11+
from check_http_json import debugPrint
12+
13+
14+
class CLITest(unittest.TestCase):
15+
"""
16+
Tests for CLI
17+
"""
18+
19+
def setUp(self):
20+
"""
21+
Defining the exitcodes
22+
"""
23+
24+
self.exit_0 = 0 << 8
25+
self.exit_1 = 1 << 8
26+
self.exit_2 = 2 << 8
27+
self.exit_3 = 3 << 8
28+
29+
def test_debugprint(self):
30+
with mock.patch('builtins.print') as mock_print:
31+
debugPrint(True, 'debug')
32+
mock_print.assert_called_once_with('debug')
33+
34+
def test_debugprint_pprint(self):
35+
with mock.patch('check_http_json.pprint') as mock_pprint:
36+
debugPrint(True, 'debug', True)
37+
mock_pprint.assert_called_once_with('debug')
38+
39+
def test_cli_without_params(self):
40+
41+
command = '/usr/bin/env python3 check_http_json.py > /dev/null 2>&1'
42+
status = os.system(command)
43+
44+
self.assertEqual(status, self.exit_2)

Diff for: test/test_main.py

+75-22
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,90 @@
88

99
sys.path.append('..')
1010

11-
from check_http_json import debugPrint
11+
from check_http_json import main
12+
13+
14+
class MockResponse():
15+
def __init__(self, status_code=200, content='{"foo": "bar"}'):
16+
self.status_code = status_code
17+
self.content = content
18+
19+
def read(self):
20+
return self.content
1221

1322

1423
class MainTest(unittest.TestCase):
1524
"""
16-
Tests for main
25+
Tests for Main
1726
"""
1827

19-
def setUp(self):
20-
"""
21-
Defining the exitcodes
22-
"""
28+
@mock.patch('builtins.print')
29+
def test_main_version(self, mock_print):
30+
args = ['--version']
31+
32+
with self.assertRaises(SystemExit) as test:
33+
main(args)
34+
35+
mock_print.assert_called_once()
36+
self.assertEqual(test.exception.code, 0)
37+
38+
@mock.patch('builtins.print')
39+
@mock.patch('urllib.request.urlopen')
40+
def test_main_with_ssl(self, mock_request, mock_print):
41+
args = '-H localhost --ssl'.split(' ')
42+
43+
mock_request.return_value = MockResponse()
44+
45+
with self.assertRaises(SystemExit) as test:
46+
main(args)
47+
48+
self.assertEqual(test.exception.code, 0)
49+
50+
51+
@mock.patch('builtins.print')
52+
@mock.patch('urllib.request.urlopen')
53+
def test_main_with_parse_error(self, mock_request, mock_print):
54+
args = '-H localhost'.split(' ')
55+
56+
mock_request.return_value = MockResponse(content='not JSON')
57+
58+
with self.assertRaises(SystemExit) as test:
59+
main(args)
60+
61+
self.assertTrue('Parser error' in str(mock_print.call_args))
62+
self.assertEqual(test.exception.code, 3)
63+
64+
@mock.patch('builtins.print')
65+
def test_main_with_url_error(self, mock_print):
66+
args = '-H localhost'.split(' ')
67+
68+
with self.assertRaises(SystemExit) as test:
69+
main(args)
70+
71+
self.assertTrue('URLError' in str(mock_print.call_args))
72+
self.assertEqual(test.exception.code, 3)
73+
74+
@mock.patch('builtins.print')
75+
@mock.patch('urllib.request.urlopen')
76+
def test_main_with_http_error_no_json(self, mock_request, mock_print):
77+
args = '-H localhost'.split(' ')
78+
79+
mock_request.return_value = MockResponse(content='not JSON', status_code=503)
2380

24-
self.exit_0 = 0 << 8
25-
self.exit_1 = 1 << 8
26-
self.exit_2 = 2 << 8
27-
self.exit_3 = 3 << 8
81+
with self.assertRaises(SystemExit) as test:
82+
main(args)
2883

29-
def test_debugprint(self):
30-
with mock.patch('builtins.print') as mock_print:
31-
debugPrint(True, 'debug')
32-
mock_print.assert_called_once_with('debug')
84+
self.assertTrue('Parser error' in str(mock_print.call_args))
85+
self.assertEqual(test.exception.code, 3)
3386

34-
def test_debugprint_pprint(self):
35-
with mock.patch('check_http_json.pprint') as mock_pprint:
36-
debugPrint(True, 'debug', True)
37-
mock_pprint.assert_called_once_with('debug')
87+
@mock.patch('builtins.print')
88+
@mock.patch('urllib.request.urlopen')
89+
def test_main_with_http_error_valid_json(self, mock_request, mock_print):
90+
args = '-H localhost'.split(' ')
3891

39-
def test_cli_without_params(self):
92+
mock_request.return_value = MockResponse(status_code=503)
4093

41-
command = '/usr/bin/env python3 check_http_json.py > /dev/null 2>&1'
42-
status = os.system(command)
94+
with self.assertRaises(SystemExit) as test:
95+
main(args)
4396

44-
self.assertEqual(status, self.exit_2)
97+
self.assertEqual(test.exception.code, 0)

0 commit comments

Comments
 (0)