From e41d82f9d2cee424fab7a102ec43fee0e28247bc Mon Sep 17 00:00:00 2001 From: Ian Fijolek Date: Mon, 4 Apr 2022 20:23:15 -0700 Subject: [PATCH] Blacken --- .pre-commit-config.yaml | 10 +-- minitor/main.py | 183 ++++++++++++++++++++-------------------- setup.py | 67 ++++++++------- tests/alert_test.py | 59 ++++++------- tests/minitor_test.py | 29 ++++--- tests/monitor_test.py | 66 ++++++++------- 6 files changed, 207 insertions(+), 207 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d258c56..66a5f07 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,17 +1,15 @@ repos: + - repo: https://github.com/psf/black + rev: 22.3.0 + hooks: + - id: black - repo: https://github.com/pre-commit/pre-commit-hooks rev: v1.2.3 hooks: - id: trailing-whitespace - id: end-of-file-fixer - - id: autopep8-wrapper - args: - - -i - - --ignore=E265,E309,E501 - id: debug-statements language_version: python3 - - id: flake8 - language_version: python3 - id: check-yaml args: - --allow-multiple-documents diff --git a/minitor/main.py b/minitor/main.py index 599f6d1..6e42433 100644 --- a/minitor/main.py +++ b/minitor/main.py @@ -16,15 +16,14 @@ from prometheus_client import start_http_server DEFAULT_METRICS_PORT = 8080 logging.basicConfig( - level=logging.ERROR, - format='%(asctime)s %(levelname)s %(name)s %(message)s' + level=logging.ERROR, format="%(asctime)s %(levelname)s %(name)s %(message)s" ) logging.getLogger(__name__).addHandler(logging.NullHandler()) def read_yaml(path): """Loads config from a YAML file with env interpolation""" - with open(path, 'r') as yaml: + with open(path, "r") as yaml: contents = yaml.read() return yamlenv.load(contents) @@ -35,44 +34,40 @@ def validate_monitor_settings(settings): Note: Cannot yet validate the Alerts exist from within this class. That will be done by Minitor later """ - name = settings.get('name') + name = settings.get("name") if not name: - raise InvalidMonitorException('Invalid name for monitor') - if not settings.get('command'): - raise InvalidMonitorException( - 'Invalid command for monitor {}'.format(name) - ) + raise InvalidMonitorException("Invalid name for monitor") + if not settings.get("command"): + raise InvalidMonitorException("Invalid command for monitor {}".format(name)) type_assertions = ( - ('check_interval', int), - ('alert_after', int), - ('alert_every', int), + ("check_interval", int), + ("alert_after", int), + ("alert_every", int), ) for key, val_type in type_assertions: val = settings.get(key) if not isinstance(val, val_type): raise InvalidMonitorException( - 'Invalid type on {}: {}. Expected {} and found {}'.format( + "Invalid type on {}: {}. Expected {} and found {}".format( name, key, val_type.__name__, type(val).__name__ ) ) non_zero = ( - 'check_interval', - 'alert_after', + "check_interval", + "alert_after", ) for key in non_zero: if settings.get(key) == 0: raise InvalidMonitorException( - 'Invalid value for {}: {}. Value cannot be 0'.format( - name, key - ) + "Invalid value for {}: {}. Value cannot be 0".format(name, key) ) -def maybe_decode(bstr, encoding='utf-8'): +def maybe_decode(bstr, encoding="utf-8"): try: return bstr.decode(encoding) except TypeError: @@ -82,14 +77,14 @@ def maybe_decode(bstr, encoding='utf-8'): def call_output(*popenargs, **kwargs): """Similar to check_output, but instead returns output and exception""" # So we can capture complete output, redirect sderr to stdout - kwargs.setdefault('stderr', subprocess.STDOUT) + kwargs.setdefault("stderr", subprocess.STDOUT) output, ex = None, None try: output = check_output(*popenargs, **kwargs) except CalledProcessError as e: output, ex = e.output, e - output = output.rstrip(b'\n') + output = output.rstrip(b"\n") return output, ex @@ -113,23 +108,23 @@ class Monitor(object): def __init__(self, config, counter=None, logger=None): """Accepts a dictionary of configuration items to override defaults""" settings = { - 'alerts': ['log'], - 'check_interval': 30, - 'alert_after': 4, - 'alert_every': -1, + "alerts": ["log"], + "check_interval": 30, + "alert_after": 4, + "alert_every": -1, } settings.update(config) validate_monitor_settings(settings) - self.name = settings['name'] - self.command = settings['command'] - self.alert_down = settings.get('alert_down', []) + self.name = settings["name"] + self.command = settings["command"] + self.alert_down = settings.get("alert_down", []) if not self.alert_down: - self.alert_down = settings.get('alerts', []) - self.alert_up = settings.get('alert_up', []) - self.check_interval = settings.get('check_interval') - self.alert_after = settings.get('alert_after') - self.alert_every = settings.get('alert_every') + self.alert_down = settings.get("alerts", []) + self.alert_up = settings.get("alert_up", []) + self.check_interval = settings.get("check_interval") + self.alert_after = settings.get("alert_after") + self.alert_every = settings.get("alert_every") self.alert_count = 0 self.last_check = None @@ -140,18 +135,18 @@ class Monitor(object): self._counter = counter if logger is None: self._logger = logging.getLogger( - '{}({})'.format(self.__class__.__name__, self.name) + "{}({})".format(self.__class__.__name__, self.name) ) else: self._logger = logger.getChild( - '{}({})'.format(self.__class__.__name__, self.name) + "{}({})".format(self.__class__.__name__, self.name) ) def _count_check(self, is_success=True, is_alert=False): if self._counter is not None: self._counter.labels( monitor=self.name, - status=('success' if is_success else 'failure'), + status=("success" if is_success else "failure"), is_alert=is_alert, ).inc() @@ -199,7 +194,7 @@ class Monitor(object): back_up = None if not self.is_up(): back_up = MinitorAlert( - '{} check is up again!'.format(self.name), + "{} check is up again!".format(self.name), self, ) self.total_failure_count = 0 @@ -215,7 +210,7 @@ class Monitor(object): if self.total_failure_count < self.alert_after: return - failure_count = (self.total_failure_count - self.alert_after) + failure_count = self.total_failure_count - self.alert_after if self.alert_every > 0: # Otherwise, we should check against our alert_every should_alert = (failure_count % self.alert_every) == 0 @@ -223,15 +218,15 @@ class Monitor(object): # Only alert on the first failure should_alert = failure_count == 1 else: - should_alert = (failure_count >= (2 ** self.alert_count) - 1) + should_alert = failure_count >= (2**self.alert_count) - 1 if should_alert: self.alert_count += 1 raise MinitorAlert( - '{} check has failed {} times'.format( + "{} check has failed {} times".format( self.name, self.total_failure_count ), - self + self, ) def is_up(self): @@ -243,18 +238,18 @@ class Alert(object): def __init__(self, name, config, counter=None, logger=None): """An alert must be named and have a config dict""" self.name = name - self.command = config.get('command') + self.command = config.get("command") if not self.command: - raise InvalidAlertException('Invalid alert {}'.format(self.name)) + raise InvalidAlertException("Invalid alert {}".format(self.name)) self._counter = counter if logger is None: self._logger = logging.getLogger( - '{}({})'.format(self.__class__.__name__, self.name) + "{}({})".format(self.__class__.__name__, self.name) ) else: self._logger = logger.getChild( - '{}({})'.format(self.__class__.__name__, self.name) + "{}({})".format(self.__class__.__name__, self.name) ) def _count_alert(self, monitor): @@ -277,7 +272,7 @@ class Alert(object): def _format_datetime(self, dt): """Formats a datetime for an alert""" if dt is None: - return 'Never' + return "Never" return dt.isoformat() def alert(self, message, monitor): @@ -313,64 +308,72 @@ class Minitor(object): def _parse_args(self, args=None): """Parses command line arguments and returns them""" - parser = ArgumentParser(description='Minimal monitoring') + parser = ArgumentParser(description="Minimal monitoring") parser.add_argument( - '--config', '-c', - dest='config_path', - default='config.yml', - help='Path to the config YAML file to use', + "--config", + "-c", + dest="config_path", + default="config.yml", + help="Path to the config YAML file to use", ) parser.add_argument( - '--metrics', '-m', - dest='metrics', - action='store_true', - help='Start webserver with metrics', + "--metrics", + "-m", + dest="metrics", + action="store_true", + help="Start webserver with metrics", ) parser.add_argument( - '--metrics-port', '-p', - dest='metrics_port', + "--metrics-port", + "-p", + dest="metrics_port", type=int, default=DEFAULT_METRICS_PORT, - help='Port to use when serving metrics', + help="Port to use when serving metrics", ) parser.add_argument( - '--verbose', '-v', - action='count', - help=('Adjust log verbosity by increasing arg count. Default log', - 'level is ERROR. Level increases with each `v`'), + "--verbose", + "-v", + action="count", + help=( + "Adjust log verbosity by increasing arg count. Default log", + "level is ERROR. Level increases with each `v`", + ), ) return parser.parse_args(args) def _setup(self, config_path): """Load all setup from YAML file at provided path""" config = read_yaml(config_path) - self.check_interval = config.get('check_interval', 30) + self.check_interval = config.get("check_interval", 30) self.monitors = [ Monitor( mon, counter=self._monitor_counter, logger=self._logger, ) - for mon in config.get('monitors', []) + for mon in config.get("monitors", []) ] # Add default alert for logging self.alerts = { - 'log': Alert( - 'log', - {'command': ['echo', '{alert_message}!']}, + "log": Alert( + "log", + {"command": ["echo", "{alert_message}!"]}, counter=self._alert_counter, logger=self._logger, ) } - self.alerts.update({ - alert_name: Alert( - alert_name, - alert, - counter=self._alert_counter, - logger=self._logger, - ) - for alert_name, alert in config.get('alerts', {}).items() - }) + self.alerts.update( + { + alert_name: Alert( + alert_name, + alert, + counter=self._alert_counter, + logger=self._logger, + ) + for alert_name, alert in config.get("alerts", {}).items() + } + ) def _validate_monitors(self): """Validates monitors are valid against other config values""" @@ -378,7 +381,7 @@ class Minitor(object): # Validate that the interval is valid if monitor.check_interval < self.check_interval: raise InvalidMonitorException( - 'Monitor {} check interval is lower global value {}'.format( + "Monitor {} check interval is lower global value {}".format( monitor.name, self.check_interval ) ) @@ -386,26 +389,26 @@ class Minitor(object): for alert in chain(monitor.alert_down, monitor.alert_up): if alert not in self.alerts: raise InvalidMonitorException( - 'Monitor {} contains an unknown alert: {}'.format( + "Monitor {} contains an unknown alert: {}".format( monitor.name, alert ) ) def _init_metrics(self): self._alert_counter = Counter( - 'minitor_alert_total', - 'Number of Minitor alerts', - ['alert', 'monitor'], + "minitor_alert_total", + "Number of Minitor alerts", + ["alert", "monitor"], ) self._monitor_counter = Counter( - 'minitor_check_total', - 'Number of Minitor checks', - ['monitor', 'status', 'is_alert'], + "minitor_check_total", + "Number of Minitor checks", + ["monitor", "status", "is_alert"], ) self._monitor_status_gauge = Gauge( - 'minitor_monitor_up_count', - 'Currently responsive monitors', - ['monitor'], + "minitor_monitor_up_count", + "Currently responsive monitors", + ["monitor"], ) def _loop(self): @@ -420,9 +423,7 @@ class Minitor(object): result = monitor.check() if result is not None: self._logger.info( - '%s: %s', - monitor.name, - 'SUCCESS' if result else 'FAILURE' + "%s: %s", monitor.name, "SUCCESS" if result else "FAILURE" ) except MinitorAlert as minitor_alert: self._logger.warning(minitor_alert) @@ -475,5 +476,5 @@ def main(args=None): return 0 -if __name__ == '__main__': +if __name__ == "__main__": sys.exit(main()) diff --git a/setup.py b/setup.py index b33b0ca..24b6349 100644 --- a/setup.py +++ b/setup.py @@ -7,47 +7,50 @@ from setuptools import setup here = path.abspath(path.dirname(__file__)) # Get the long description from the README file -with open(path.join(here, 'README.md'), encoding='utf-8') as f: +with open(path.join(here, "README.md"), encoding="utf-8") as f: long_description = f.read() setup( - name='minitor', - version='1.0.3', - description='A minimal monitoring tool', + name="minitor", + version="1.0.3", + description="A minimal monitoring tool", long_description=long_description, - long_description_content_type='text/markdown', - url='https://git.iamthefij.com/iamthefij/minitor', - download_url=( - 'https://git.iamthefij.com/iamthefij/minitor/archive/master.tar.gz' - ), - author='Ian Fijolek', - author_email='ian@iamthefij.com', + long_description_content_type="text/markdown", + url="https://git.iamthefij.com/iamthefij/minitor", + download_url=("https://git.iamthefij.com/iamthefij/minitor/archive/master.tar.gz"), + author="Ian Fijolek", + author_email="ian@iamthefij.com", classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Intended Audience :: System Administrators', - 'Topic :: System :: Monitoring', - 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: System Administrators", + "Topic :: System :: Monitoring", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", ], - keywords='minitor monitoring alerting', - packages=find_packages(exclude=[ - 'contrib', - 'docs', - 'examples', - 'scripts', - 'tests', - ]), + keywords="minitor monitoring alerting", + packages=find_packages( + exclude=[ + "contrib", + "docs", + "examples", + "scripts", + "tests", + ] + ), install_requires=[ - 'prometheus_client', - 'yamlenv', + "prometheus_client", + "yamlenv", ], + scripts=["scripts/docker_check.sh"], entry_points={ - 'console_scripts': [ - 'minitor=minitor.main:main', + "console_scripts": [ + "minitor=minitor.main:main", ], }, ) diff --git a/tests/alert_test.py b/tests/alert_test.py index 90b9c90..45293b3 100644 --- a/tests/alert_test.py +++ b/tests/alert_test.py @@ -9,54 +9,47 @@ from tests.util import assert_called_once_with class TestAlert(object): - @pytest.fixture def monitor(self): - return Monitor({ - 'name': 'Dummy Monitor', - 'command': ['echo', 'foo'], - }) + return Monitor( + { + "name": "Dummy Monitor", + "command": ["echo", "foo"], + } + ) @pytest.fixture def echo_alert(self): return Alert( - 'log', + "log", { - 'command': [ - 'echo', ( - '{monitor_name} has failed {failure_count} time(s)!\n' - 'We have alerted {alert_count} time(s)\n' - 'Last success was {last_success}\n' - 'Last output was: {last_output}' - ) + "command": [ + "echo", + ( + "{monitor_name} has failed {failure_count} time(s)!\n" + "We have alerted {alert_count} time(s)\n" + "Last success was {last_success}\n" + "Last output was: {last_output}" + ), ] - } + }, ) @pytest.mark.parametrize( - 'last_success,expected_success', - [ - (None, 'Never'), - (datetime(2018, 4, 10), '2018-04-10T00:00:00') - ] + "last_success,expected_success", + [(None, "Never"), (datetime(2018, 4, 10), "2018-04-10T00:00:00")], ) - def test_simple_alert( - self, - monitor, - echo_alert, - last_success, - expected_success - ): + def test_simple_alert(self, monitor, echo_alert, last_success, expected_success): monitor.alert_count = 1 - monitor.last_output = 'beep boop' + monitor.last_output = "beep boop" monitor.last_success = last_success monitor.total_failure_count = 1 - with patch.object(echo_alert._logger, 'error') as mock_error: - echo_alert.alert('Exception message', monitor) + with patch.object(echo_alert._logger, "error") as mock_error: + echo_alert.alert("Exception message", monitor) assert_called_once_with( mock_error, - 'Dummy Monitor has failed 1 time(s)!\n' - 'We have alerted 1 time(s)\n' - 'Last success was ' + expected_success + '\n' - 'Last output was: beep boop' + "Dummy Monitor has failed 1 time(s)!\n" + "We have alerted 1 time(s)\n" + "Last success was " + expected_success + "\n" + "Last output was: beep boop", ) diff --git a/tests/minitor_test.py b/tests/minitor_test.py index 5fa2c9e..4e7f6aa 100644 --- a/tests/minitor_test.py +++ b/tests/minitor_test.py @@ -6,30 +6,31 @@ from minitor.main import Minitor class TestMinitor(object): - def test_call_output(self): # valid command should have result and no exception - output, ex = call_output(['echo', 'test']) - assert output == b'test' + output, ex = call_output(["echo", "test"]) + assert output == b"test" assert ex is None - output, ex = call_output(['ls', '--not-a-real-flag']) - assert output.startswith(b'ls: ') + output, ex = call_output(["ls", "--not-a-real-flag"]) + assert output.startswith(b"ls: ") assert ex is not None def test_run(self): """Doesn't really check much, but a simple integration sanity test""" test_loop_count = 5 - os.environ.update({ - 'MAILGUN_API_KEY': 'test-mg-key', - 'AVAILABLE_NUMBER': '555-555-5050', - 'MY_PHONE': '555-555-0505', - 'ACCOUNT_SID': 'test-account-id', - 'AUTH_TOKEN': 'test-account-token', - }) - args = '--config ./sample-config.yml'.split(' ') + os.environ.update( + { + "MAILGUN_API_KEY": "test-mg-key", + "AVAILABLE_NUMBER": "555-555-5050", + "MY_PHONE": "555-555-0505", + "ACCOUNT_SID": "test-account-id", + "AUTH_TOKEN": "test-account-token", + } + ) + args = "--config ./sample-config.yml".split(" ") minitor = Minitor() - with patch.object(minitor, '_loop'): + with patch.object(minitor, "_loop"): minitor.run(args) # Skip the loop, but run a single check for _ in range(test_loop_count): diff --git a/tests/monitor_test.py b/tests/monitor_test.py index 1c8cfe7..e8dcc2a 100644 --- a/tests/monitor_test.py +++ b/tests/monitor_test.py @@ -11,40 +11,44 @@ from tests.util import assert_called_once class TestMonitor(object): - @pytest.fixture def monitor(self): - return Monitor({ - 'name': 'Sample Monitor', - 'command': ['echo', 'foo'], - 'alert_down': ['log'], - 'alert_up': ['log'], - 'check_interval': 1, - 'alert_after': 1, - 'alert_every': 1, - }) + return Monitor( + { + "name": "Sample Monitor", + "command": ["echo", "foo"], + "alert_down": ["log"], + "alert_up": ["log"], + "check_interval": 1, + "alert_after": 1, + "alert_every": 1, + } + ) - @pytest.mark.parametrize('settings', [ - {'alert_after': 0}, - {'alert_every': 0}, - {'check_interval': 0}, - {'alert_after': 'invalid'}, - {'alert_every': 'invalid'}, - {'check_interval': 'invalid'}, - ]) + @pytest.mark.parametrize( + "settings", + [ + {"alert_after": 0}, + {"alert_every": 0}, + {"check_interval": 0}, + {"alert_after": "invalid"}, + {"alert_every": "invalid"}, + {"check_interval": "invalid"}, + ], + ) def test_monitor_invalid_configuration(self, settings): with pytest.raises(InvalidMonitorException): validate_monitor_settings(settings) @pytest.mark.parametrize( - 'alert_after', + "alert_after", [1, 20], - ids=lambda arg: 'alert_after({})'.format(arg), + ids=lambda arg: "alert_after({})".format(arg), ) @pytest.mark.parametrize( - 'alert_every', + "alert_every", [-1, 1, 2, 1440], - ids=lambda arg: 'alert_every({})'.format(arg), + ids=lambda arg: "alert_every({})".format(arg), ) def test_monitor_alert_after(self, monitor, alert_after, alert_every): monitor.alert_after = alert_after @@ -59,14 +63,14 @@ class TestMonitor(object): monitor.failure() @pytest.mark.parametrize( - 'alert_after', + "alert_after", [1, 20], - ids=lambda arg: 'alert_after({})'.format(arg), + ids=lambda arg: "alert_after({})".format(arg), ) @pytest.mark.parametrize( - 'alert_every', + "alert_every", [1, 2, 1440], - ids=lambda arg: 'alert_every({})'.format(arg), + ids=lambda arg: "alert_every({})".format(arg), ) def test_monitor_alert_every(self, monitor, alert_after, alert_every): monitor.alert_after = alert_after @@ -102,27 +106,27 @@ class TestMonitor(object): else: monitor.failure() - @pytest.mark.parametrize('last_check', [None, datetime(2018, 4, 10)]) + @pytest.mark.parametrize("last_check", [None, datetime(2018, 4, 10)]) def test_monitor_should_check(self, monitor, last_check): monitor.last_check = last_check assert monitor.should_check() def test_monitor_check_fail(self, monitor): assert monitor.last_output is None - with patch.object(monitor, 'failure') as mock_failure: - monitor.command = ['ls', '--not-real'] + with patch.object(monitor, "failure") as mock_failure: + monitor.command = ["ls", "--not-real"] assert not monitor.check() assert_called_once(mock_failure) assert monitor.last_output is not None def test_monitor_check_success(self, monitor): assert monitor.last_output is None - with patch.object(monitor, 'success') as mock_success: + with patch.object(monitor, "success") as mock_success: assert monitor.check() assert_called_once(mock_success) assert monitor.last_output is not None - @pytest.mark.parametrize('failure_count', [0, 1]) + @pytest.mark.parametrize("failure_count", [0, 1]) def test_monitor_success(self, monitor, failure_count): monitor.alert_count = 0 monitor.total_failure_count = failure_count