diff --git a/cachet_url_monitor/configuration.py b/cachet_url_monitor/configuration.py index 6a8087b..ee6f008 100644 --- a/cachet_url_monitor/configuration.py +++ b/cachet_url_monitor/configuration.py @@ -25,6 +25,16 @@ def __str__(self): return repr(self.value) +class ComponentNonexistentError(Exception): + """Exception raised when the component does not exist.""" + + def __init__(self, component_id): + self.component_id = component_id + + def __str__(self): + return repr('Component with id [%d] does not exist.' % (self.component_id,)) + + class Configuration(object): """Represents a configuration file, but it also includes the functionality of assessing the API and pushing the results to cachet. @@ -37,7 +47,10 @@ def __init__(self, config_file): self.config_file = config_file self.data = load(file(self.config_file, 'r')) + # We need to validate the configuration is correct and then validate the component actually exists. self.validate() + self.headers = {'X-Cachet-Token': self.data['cachet']['token']} + self.status = self.get_current_status(self.data['cachet']['component_id']) self.logger.info('Monitoring URL: %s %s' % (self.data['endpoint']['method'], self.data['endpoint']['url'])) @@ -46,7 +59,16 @@ def __init__(self, config_file): for expectation in self.expectations: self.logger.info('Registered expectation: %s' % (expectation,)) - self.headers = {'X-Cachet-Token': self.data['cachet']['token']} + def get_current_status(self, component_id): + get_status_request = requests.get( + '%s/components/%d' % (self.data['cachet']['api_url'], self.data['cachet']['component_id']), + headers=self.headers) + + if get_status_request.ok: + # The component exists. + return get_status_request.json()['data']['status'] + else: + raise ComponentNonexistentError(component_id) def is_create_incident(self): """Will verify if the configuration is set to create incidents or not. @@ -172,7 +194,8 @@ def push_incident(self): if incident_request.ok: # Successful metrics upload self.logger.info( - 'Incident updated: component status [%d], message: "%s"' % (self.status, self.message)) + 'Incident updated, API healthy again: component status [%d], message: "%s"' % ( + self.status, self.message)) del self.incident_id else: self.logger.warning( @@ -190,7 +213,7 @@ def push_incident(self): self.incident_id = incident_request.json()['data']['id'] self.logger.info( 'Incident uploaded, API unhealthy: component status [%d], message: "%s"' % ( - self.status, self.message)) + self.status, self.message)) else: self.logger.warning( 'Incident upload failed with status [%d], message: "%s"' % ( @@ -253,10 +276,10 @@ def get_status(self, response): return cachet_url_monitor.status.COMPONENT_STATUS_PERFORMANCE_ISSUES def get_message(self, response): - return 'Latency above threshold: %.4f' % (response.elapsed.total_seconds(),) + return 'Latency above threshold: %.4f seconds' % (response.elapsed.total_seconds(),) def __str__(self): - return repr('Latency threshold: %.4f' % (self.threshold,)) + return repr('Latency threshold: %.4f seconds' % (self.threshold,)) class Regex(Expectaction): diff --git a/cachet_url_monitor/scheduler.py b/cachet_url_monitor/scheduler.py index 7fae92c..37c6bb1 100644 --- a/cachet_url_monitor/scheduler.py +++ b/cachet_url_monitor/scheduler.py @@ -10,6 +10,7 @@ class Agent(object): """Monitor agent that will be constantly verifying if the URL is healthy and updating the component. """ + def __init__(self, configuration): self.configuration = configuration @@ -18,7 +19,6 @@ def execute(self): cachet server. """ self.configuration.evaluate() - self.configuration.push_status() self.configuration.push_metrics() def start(self): @@ -26,11 +26,34 @@ def start(self): schedule.every(self.configuration.data['frequency']).seconds.do(self.execute) +class UpdateStatusAgent(Agent): + def __init__(self, configuration): + super(UpdateStatusAgent, self).__init__(configuration) + + def execute(self): + super(UpdateStatusAgent, self).execute() + self.configuration.push_status() + + +class CreateIncidentAgent(Agent): + def __init__(self, configuration): + super(CreateIncidentAgent, self).__init__(configuration) + + def execute(self): + super(CreateIncidentAgent, self).execute() + self.configuration.push_incident() + + class Scheduler(object): def __init__(self, config_file): self.logger = logging.getLogger('cachet_url_monitor.scheduler.Scheduler') self.configuration = Configuration(config_file) - self.agent = Agent(self.configuration) + + if self.configuration.is_create_incident(): + self.agent = CreateIncidentAgent(self.configuration) + else: + self.agent = UpdateStatusAgent(self.configuration) + self.stop = False def start(self): diff --git a/config.yml b/config.yml index 5ba9817..07d6f73 100644 --- a/config.yml +++ b/config.yml @@ -1,7 +1,7 @@ endpoint: url: http://localhost:8080/swagger method: GET - timeout: 0.01 + timeout: 0.1 expectation: - type: HTTP_STATUS status: 200 diff --git a/tests/test_expectation.py b/tests/test_expectation.py index 8ac191c..6469448 100644 --- a/tests/test_expectation.py +++ b/tests/test_expectation.py @@ -42,7 +42,7 @@ def total_seconds(): elapsed.total_seconds = total_seconds assert self.expectation.get_message(request) == ('Latency above ' - 'threshold: 0.1000') + 'threshold: 0.1000 seconds') class HttpStatusTest(unittest.TestCase): diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py index 6f1bd56..ec3945b 100644 --- a/tests/test_scheduler.py +++ b/tests/test_scheduler.py @@ -1,10 +1,13 @@ #!/usr/bin/env python -import mock -import unittest import sys +import unittest + +import mock + sys.modules['schedule'] = mock.Mock() -sys.modules['cachet_url_monitor.configuration.Configuration'] = mock.Mock() -from cachet_url_monitor.scheduler import Agent,Scheduler +# sys.modules['cachet_url_monitor.configuration.Configuration'] = mock.Mock() +sys.modules['requests'] = mock.Mock() +from cachet_url_monitor.scheduler import Agent, Scheduler class AgentTest(unittest.TestCase): @@ -21,7 +24,7 @@ def test_execute(self): self.agent.execute() evaluate.assert_called_once() - push_status.assert_called_once() + push_status.assert_not_called() def test_start(self): every = sys.modules['schedule'].every @@ -33,16 +36,28 @@ def test_start(self): class SchedulerTest(unittest.TestCase): + @mock.patch('cachet_url_monitor.configuration.Configuration.__init__', mock.Mock(return_value=None)) + @mock.patch('cachet_url_monitor.configuration.Configuration.is_create_incident', mock.Mock(return_value=False)) def setUp(self): - self.mock_configuration = sys.modules[('cachet_url_monitor.configuration' - '.Configuration')] + def get(url, headers): + get_return = mock.Mock() + get_return.ok = True + get_return.json = mock.Mock() + get_return.json.return_value = {'data': {'status': 1}} + return get_return + + sys.modules['requests'].get = get + self.scheduler = Scheduler('config.yml') def test_init(self): assert self.scheduler.stop == False - def test_start(self): - #TODO(mtakaki|2016-05-01): We need a better way of testing this method. + @mock.patch('cachet_url_monitor.configuration.Configuration', create=True) + def test_start(self, mock_configuration): + # TODO(mtakaki|2016-05-01): We need a better way of testing this method. # Leaving it as a placeholder. + mock_configuration.data = {'frequency': 30} + self.scheduler.stop = True self.scheduler.start()