-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#6: Implement "docker-container-log" check
- Loading branch information
1 parent
ad3e821
commit e266dc9
Showing
3 changed files
with
180 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
#!/usr/bin/env python3 | ||
|
||
""" | ||
<sphinx> | ||
docker-container-log | ||
-------------------- | ||
Searches docker container logs for matching given regular expression. | ||
Parameters: | ||
- container: Docker container name | ||
- regexp: Regular expression | ||
- max_lines: Number of last lines to check (defaults to 5) | ||
- since_seconds: Get only logs since this time (eg. last 5 minutes = 5 * 60 = 300) (defaults to 300) | ||
- present: Boolean, if the string should be present in the output or not | ||
</sphinx> | ||
""" | ||
|
||
import os | ||
import sys | ||
import re | ||
from subprocess import check_output | ||
from subprocess import CalledProcessError | ||
from subprocess import STDOUT | ||
|
||
|
||
def parse_bool(boolean: str) -> bool: | ||
return bool(int(boolean.lower().replace('true', "1").replace('false', "0"))) | ||
|
||
|
||
class DockerContainerLogCheck(object): | ||
def _check_output(self, *args, **kwargs): | ||
return check_output(*args, **kwargs) | ||
|
||
def main(self, container: str, regexp: str, max_lines: int, since: int, should_be_present: bool): | ||
opts = [] | ||
|
||
if since > 0: | ||
opts.append('--since={}s'.format(since)) | ||
|
||
try: | ||
out = self._check_output( | ||
['docker', 'logs', container, '--tail="{}"'.format(max_lines)] + opts, stderr=STDOUT).decode('utf-8') | ||
|
||
except CalledProcessError as e: | ||
out = e.output.decode('utf-8') | ||
|
||
is_present_in_output = re.findall(regexp, out) | ||
|
||
if should_be_present and is_present_in_output: | ||
return True, "The container last {} lines of output has a match, as expected".format(max_lines) | ||
|
||
if not should_be_present and is_present_in_output: | ||
return False, "The container output is matching the pattern but should not, " \ | ||
"looked at {} lines since {} in {}"\ | ||
.format(max_lines, str(since) + 's', container) | ||
|
||
if should_be_present and not is_present_in_output: | ||
return False, "The container last {} lines of output are not matching, expecting they were"\ | ||
.format(max_lines) | ||
|
||
if not should_be_present and not is_present_in_output: | ||
return True, "The container last {} lines of output are not matching, as expected".format(max_lines) | ||
|
||
return False, "Unknown error" | ||
|
||
|
||
if __name__ == '__main__': | ||
app = DockerContainerLogCheck() | ||
|
||
try: | ||
status, message = app.main( | ||
container=os.environ['CONTAINER'], | ||
regexp=os.environ['REGEXP'], | ||
max_lines=int(os.getenv('MAX_LINES', '5')), | ||
since=int(os.getenv('SINCE_SECONDS', '300')), | ||
should_be_present=parse_bool(os.environ['PRESENT']) | ||
) | ||
except KeyError as attribute: | ||
print('Missing environment variable: {}'.format(attribute)) | ||
sys.exit(1) | ||
|
||
print(message) | ||
sys.exit(0 if status else 1) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import unittest | ||
import os | ||
import inspect | ||
from typing import Tuple | ||
|
||
# import a script with "-" in filename | ||
path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) + '/../' | ||
with open(path + '/infracheck/checks/docker-container-log', 'r') as script: | ||
exec(script.read()) | ||
|
||
|
||
class DockerContainerLogCheckTest(unittest.TestCase): | ||
@staticmethod | ||
def _run_mocked_check(container: str, regexp: str, max_lines: int, since: int, should_be_present: bool) \ | ||
-> Tuple[bool, str, list]: | ||
|
||
check = DockerContainerLogCheck() | ||
called = [] | ||
|
||
def check_output(*args, **kwargs): | ||
called.append(args) | ||
|
||
return b""" | ||
Hello | ||
World | ||
This | ||
Is | ||
A | ||
Test | ||
""" | ||
|
||
check._check_output = check_output | ||
result, message = check.main( | ||
container, | ||
regexp, | ||
max_lines, | ||
since, | ||
should_be_present | ||
) | ||
|
||
return result, message, called | ||
|
||
def test_expecting_exact_match_and_have_match(self) -> None: | ||
result, message, called = self._run_mocked_check( | ||
container='container_1', | ||
regexp='Hello', | ||
max_lines=15, | ||
since=5, | ||
should_be_present=True | ||
) | ||
|
||
self.assertTrue(result) | ||
self.assertEqual('The container last 15 lines of output has a match, as expected', message) | ||
|
||
def test_expecting_that_will_not_match(self) -> None: | ||
result, message, called = self._run_mocked_check( | ||
container='container_1', | ||
regexp='This text will not be found', | ||
max_lines=15, | ||
since=5, | ||
should_be_present=True | ||
) | ||
|
||
self.assertFalse(result) | ||
self.assertEqual('The container last 15 lines of output are not matching, expecting they were', message) | ||
|
||
def test_expecting_to_not_find_text_and_will_not_find(self) -> None: | ||
result, message, called = self._run_mocked_check( | ||
container='container_1', | ||
regexp='This text will not be found', | ||
max_lines=15, | ||
since=5, | ||
should_be_present=False | ||
) | ||
|
||
self.assertTrue(result) | ||
self.assertEqual('The container last 15 lines of output are not matching, as expected', message) | ||
|
||
def test_expecting_to_not_find_but_will_find(self) -> None: | ||
result, message, called = self._run_mocked_check( | ||
container='container_1', | ||
regexp='Hello', | ||
max_lines=15, | ||
since=5, | ||
should_be_present=False | ||
) | ||
|
||
self.assertFalse(result) | ||
self.assertEqual('The container output is matching the pattern but should not, ' | ||
'looked at 15 lines since 5s in container_1', message) |