Don't crash when receiving unknown configuration variables
Instead of crashing if the monkey deserializes an unknown configuration variable, send an error message to the current monkey server and keep on working. Add utnittests. fixes #26
This commit is contained in:
parent
569a9b083e
commit
f1dca7fa86
|
@ -1 +0,0 @@
|
|||
__author__ = 'itamar'
|
|
@ -45,6 +45,11 @@ def _cast_by_example(value, example):
|
|||
|
||||
class Configuration(object):
|
||||
def from_dict(self, data):
|
||||
"""
|
||||
Get a dict of config variables, set known variables as attributes on self.
|
||||
Return dict of unknown variables encountered.
|
||||
"""
|
||||
unknown_variables = {}
|
||||
for key, value in data.items():
|
||||
if key.startswith('_'):
|
||||
continue
|
||||
|
@ -55,9 +60,11 @@ class Configuration(object):
|
|||
try:
|
||||
default_value = getattr(Configuration, key)
|
||||
except AttributeError:
|
||||
raise
|
||||
unknown_variables[key] = value
|
||||
continue
|
||||
|
||||
setattr(self, key, _cast_by_example(value, default_value))
|
||||
return unknown_variables
|
||||
|
||||
def as_dict(self):
|
||||
result = {}
|
||||
|
|
|
@ -124,7 +124,7 @@ class ControlClient(object):
|
|||
return
|
||||
|
||||
try:
|
||||
WormConfiguration.from_dict(reply.json().get('config'))
|
||||
unknown_variables = WormConfiguration.from_dict(reply.json().get('config'))
|
||||
LOG.info("New configuration was loaded from server: %r" % (WormConfiguration.as_dict(),))
|
||||
except Exception, exc:
|
||||
# we don't continue with default conf here because it might be dangerous
|
||||
|
@ -132,6 +132,23 @@ class ControlClient(object):
|
|||
WormConfiguration.current_server, reply._content, exc)
|
||||
raise Exception("Couldn't load from from server's configuration, aborting. %s" % exc)
|
||||
|
||||
if unknown_variables:
|
||||
ControlClient.send_config_error()
|
||||
|
||||
@staticmethod
|
||||
def send_config_error():
|
||||
if not WormConfiguration.current_server:
|
||||
return
|
||||
try:
|
||||
requests.patch("https://%s/api/monkey/%s" % (WormConfiguration.current_server, GUID),
|
||||
data=json.dumps({'config_error': True}),
|
||||
headers={'content-type': 'application/json'},
|
||||
verify=False,
|
||||
proxies=ControlClient.proxies)
|
||||
except Exception, exc:
|
||||
LOG.warn("Error connecting to control server %s: %s", WormConfiguration.current_server, exc)
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def check_for_stop():
|
||||
ControlClient.load_control_config()
|
||||
|
|
|
@ -12,3 +12,5 @@ psutil
|
|||
PyInstaller
|
||||
ecdsa
|
||||
netifaces
|
||||
mock
|
||||
nose
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
# -*- coding: UTF-8 -*-
|
||||
# NOTE: Launch all tests with `nosetests` command from chaos_monkey dir.
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
from mock import Mock, patch
|
||||
|
||||
import control
|
||||
|
||||
from config import GUID
|
||||
|
||||
|
||||
class ReportConfigErrorTestCase(unittest.TestCase):
|
||||
"""
|
||||
When unknown config variable received form the island server, skip it and report config
|
||||
error back to the server.
|
||||
"""
|
||||
|
||||
config_response = Mock(json=Mock(return_value={'config': {'blah': 'blah'}}))
|
||||
|
||||
def teardown(self):
|
||||
patch.stopall()
|
||||
|
||||
def test_config(self):
|
||||
patch('control.requests.patch', Mock()).start()
|
||||
patch('control.WormConfiguration', Mock(current_server='127.0.0.1:123')).start()
|
||||
|
||||
# GIVEN the server with uknown config variable
|
||||
patch('control.requests.get', Mock(return_value=self.config_response)).start()
|
||||
|
||||
# WHEN monkey tries to load config from server
|
||||
control.ControlClient.load_control_config()
|
||||
|
||||
# THEN she reports config error back to the server
|
||||
control.requests.patch.assert_called_once_with(
|
||||
"https://127.0.0.1:123/api/monkey/%s" % GUID,
|
||||
data=json.dumps({'config_error': True}),
|
||||
headers={'content-type': 'application/json'},
|
||||
verify=False,
|
||||
proxies=control.ControlClient.proxies)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -86,6 +86,8 @@ class Monkey(restful.Resource):
|
|||
update['$set']['config'] = monkey_json['config']
|
||||
if 'tunnel' in monkey_json:
|
||||
update['$set']['tunnel'] = monkey_json['tunnel']
|
||||
if 'config_error' in monkey_json:
|
||||
update['$set']['config_error'] = monkey_json['config_error']
|
||||
|
||||
return mongo.db.monkey.update({"guid": guid}, update, upsert=False)
|
||||
|
||||
|
|
Loading…
Reference in New Issue