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
# 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):
"""Represents a configuration file, but it also includes the functionality
of assessing the API and pushing the results to cachet.
"""
def __init__(self, config_file):
#TODO(mtakaki|2016-04-26): Needs validation if the config is correct.
#TODO(mtakaki|2016-04-28): Accept overriding settings using environment
#TODO(mtakaki#1|2016-04-28): Accept overriding settings using environment
# variables so we have a more docker-friendly approach.
self.logger = logging.getLogger('cachet_url_monitor.configuration.Configuration')
self.config_file = config_file
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
in self.data['endpoint']['expectation']]
for expectation in self.expectations:
self.logger.info('Registered expectation: %s' % (expectation,))
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):
"""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
@@ -132,6 +175,9 @@ class HttpStatus(Expectaction):
def get_message(self, response):
return 'Unexpected HTTP status (%s)' % (response.status_code,)
def __str__(self):
return repr('HTTP status: %s' % (self.status,))
class Latency(Expectaction):
def __init__(self, configuration):
@@ -146,9 +192,13 @@ class Latency(Expectaction):
def get_message(self, response):
return 'Latency above threshold: %.4f' % (response.elapsed.total_seconds(),)
def __str__(self):
return repr('Latency threshold: %.4f' % (self.threshold,))
class Regex(Expectaction):
def __init__(self, configuration):
self.regex_string = configuration['regex']
self.regex = re.compile(configuration['regex'])
def get_status(self, response):
@@ -159,3 +209,6 @@ class Regex(Expectaction):
def get_message(self, response):
return 'Regex did not match anything in the body'
def __str__(self):
return repr('Regex: %s' % (self.regex_string,))