Merge pull request #38 from Fak3/f26

Don't crash when receiving unknown configuration variables
This commit is contained in:
Daniel Goldberg 2016-10-08 08:33:33 -06:00 committed by GitHub
commit 3ef24281a7
7 changed files with 75 additions and 3 deletions

View File

@ -1 +0,0 @@
__author__ = 'itamar'

View File

@ -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 = {}

View File

@ -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()

View File

@ -12,3 +12,5 @@ psutil
PyInstaller
ecdsa
netifaces
mock
nose

View File

View File

@ -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()

View File

@ -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)