Skip to content

Commit

Permalink
Verifying component status when we initialize to set the initial stat…
Browse files Browse the repository at this point in the history
…e. Tests are failing, but checking it in nonetheless.
  • Loading branch information
mtakaki committed May 18, 2016
1 parent 0f53ff8 commit ca358ea
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 18 deletions.
33 changes: 28 additions & 5 deletions cachet_url_monitor/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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']))
Expand All @@ -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.
Expand Down Expand Up @@ -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(
Expand All @@ -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"' % (
Expand Down Expand Up @@ -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):
Expand Down
27 changes: 25 additions & 2 deletions cachet_url_monitor/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -18,19 +19,41 @@ def execute(self):
cachet server.
"""
self.configuration.evaluate()
self.configuration.push_status()
self.configuration.push_metrics()

def start(self):
"""Sets up the schedule based on the configuration file."""
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):
Expand Down
2 changes: 1 addition & 1 deletion config.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
endpoint:
url: http://localhost:8080/swagger
method: GET
timeout: 0.01
timeout: 0.1
expectation:
- type: HTTP_STATUS
status: 200
Expand Down
2 changes: 1 addition & 1 deletion tests/test_expectation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
33 changes: 24 additions & 9 deletions tests/test_scheduler.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -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
Expand All @@ -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()

0 comments on commit ca358ea

Please sign in to comment.