Merge pull request #5 from mtakaki/mtakaki_validate_configuration

Validate configuration
This commit is contained in:
mtakaki
2016-05-03 23:46:07 -07:00

View File

@@ -7,23 +7,66 @@ import time
from yaml import load from yaml import load
# This is the mandatory fields that must be in the configuration file in this
# same exact structure.
configuration_mandatory_fields = {
'endpoint': ['url', 'method', 'timeout', 'expectation'],
'cachet': ['api_url', 'token', 'component_id'],
'frequency': []}
class ConfigurationValidationError(Exception):
"""Exception raised when there's a validation error."""
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
class Configuration(object): class Configuration(object):
"""Represents a configuration file, but it also includes the functionality """Represents a configuration file, but it also includes the functionality
of assessing the API and pushing the results to cachet. of assessing the API and pushing the results to cachet.
""" """
def __init__(self, config_file): def __init__(self, config_file):
#TODO(mtakaki|2016-04-26): Needs validation if the config is correct. #TODO(mtakaki#1|2016-04-28): Accept overriding settings using environment
#TODO(mtakaki|2016-04-28): Accept overriding settings using environment
# variables so we have a more docker-friendly approach. # variables so we have a more docker-friendly approach.
self.logger = logging.getLogger('cachet_url_monitor.configuration.Configuration') self.logger = logging.getLogger('cachet_url_monitor.configuration.Configuration')
self.config_file = config_file self.config_file = config_file
self.data = load(file(self.config_file, 'r')) self.data = load(file(self.config_file, 'r'))
self.validate()
self.logger.info('Monitoring URL: %s %s' %
(self.data['endpoint']['method'], self.data['endpoint']['url']))
self.expectations = [Expectaction.create(expectation) for expectation self.expectations = [Expectaction.create(expectation) for expectation
in self.data['endpoint']['expectation']] in self.data['endpoint']['expectation']]
for expectation in self.expectations: for expectation in self.expectations:
self.logger.info('Registered expectation: %s' % (expectation,)) self.logger.info('Registered expectation: %s' % (expectation,))
self.headers = {'X-Cachet-Token': self.data['cachet']['token']} self.headers = {'X-Cachet-Token': self.data['cachet']['token']}
def validate(self):
configuration_errors = []
for key, sub_entries in configuration_mandatory_fields.iteritems():
if key not in self.data:
configuration_errors.append(key)
for sub_key in sub_entries:
if sub_key not in self.data[key]:
configuration_errors.append('%s.%s' % (key, sub_key))
if ('endpoint' in self.data and 'expectation' in
self.data['endpoint']):
if (not isinstance(self.data['endpoint']['expectation'], list) or
(isinstance(self.data['endpoint']['expectation'], list) and
len(self.data['endpoint']['expectation']) == 0)):
configuration_errors.append('endpoint.expectation')
if len(configuration_errors) > 0:
raise ConfigurationValidationError(('Config file [%s] failed '
'validation. Missing keys: %s') % (self.config_file,
', '.join(configuration_errors)))
def evaluate(self): def evaluate(self):
"""Sends the request to the URL set in the configuration and executes """Sends the request to the URL set in the configuration and executes
each one of the expectations, one by one. The status will be updated each one of the expectations, one by one. The status will be updated
@@ -132,6 +175,9 @@ class HttpStatus(Expectaction):
def get_message(self, response): def get_message(self, response):
return 'Unexpected HTTP status (%s)' % (response.status_code,) return 'Unexpected HTTP status (%s)' % (response.status_code,)
def __str__(self):
return repr('HTTP status: %s' % (self.status,))
class Latency(Expectaction): class Latency(Expectaction):
def __init__(self, configuration): def __init__(self, configuration):
@@ -146,9 +192,13 @@ class Latency(Expectaction):
def get_message(self, response): def get_message(self, response):
return 'Latency above threshold: %.4f' % (response.elapsed.total_seconds(),) return 'Latency above threshold: %.4f' % (response.elapsed.total_seconds(),)
def __str__(self):
return repr('Latency threshold: %.4f' % (self.threshold,))
class Regex(Expectaction): class Regex(Expectaction):
def __init__(self, configuration): def __init__(self, configuration):
self.regex_string = configuration['regex']
self.regex = re.compile(configuration['regex']) self.regex = re.compile(configuration['regex'])
def get_status(self, response): def get_status(self, response):
@@ -159,3 +209,6 @@ class Regex(Expectaction):
def get_message(self, response): def get_message(self, response):
return 'Regex did not match anything in the body' return 'Regex did not match anything in the body'
def __str__(self):
return repr('Regex: %s' % (self.regex_string,))