forked from p15670423/monkey
Merge pull request #38 from Fak3/f26
Don't crash when receiving unknown configuration variables
This commit is contained in:
commit
3ef24281a7
|
@ -1 +0,0 @@
|
||||||
__author__ = 'itamar'
|
|
|
@ -45,6 +45,11 @@ def _cast_by_example(value, example):
|
||||||
|
|
||||||
class Configuration(object):
|
class Configuration(object):
|
||||||
def from_dict(self, data):
|
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():
|
for key, value in data.items():
|
||||||
if key.startswith('_'):
|
if key.startswith('_'):
|
||||||
continue
|
continue
|
||||||
|
@ -55,9 +60,11 @@ class Configuration(object):
|
||||||
try:
|
try:
|
||||||
default_value = getattr(Configuration, key)
|
default_value = getattr(Configuration, key)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise
|
unknown_variables[key] = value
|
||||||
|
continue
|
||||||
|
|
||||||
setattr(self, key, _cast_by_example(value, default_value))
|
setattr(self, key, _cast_by_example(value, default_value))
|
||||||
|
return unknown_variables
|
||||||
|
|
||||||
def as_dict(self):
|
def as_dict(self):
|
||||||
result = {}
|
result = {}
|
||||||
|
|
|
@ -124,7 +124,7 @@ class ControlClient(object):
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
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(),))
|
LOG.info("New configuration was loaded from server: %r" % (WormConfiguration.as_dict(),))
|
||||||
except Exception, exc:
|
except Exception, exc:
|
||||||
# we don't continue with default conf here because it might be dangerous
|
# 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)
|
WormConfiguration.current_server, reply._content, exc)
|
||||||
raise Exception("Couldn't load from from server's configuration, aborting. %s" % 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
|
@staticmethod
|
||||||
def check_for_stop():
|
def check_for_stop():
|
||||||
ControlClient.load_control_config()
|
ControlClient.load_control_config()
|
||||||
|
|
|
@ -12,3 +12,5 @@ psutil
|
||||||
PyInstaller
|
PyInstaller
|
||||||
ecdsa
|
ecdsa
|
||||||
netifaces
|
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']
|
update['$set']['config'] = monkey_json['config']
|
||||||
if 'tunnel' in monkey_json:
|
if 'tunnel' in monkey_json:
|
||||||
update['$set']['tunnel'] = monkey_json['tunnel']
|
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)
|
return mongo.db.monkey.update({"guid": guid}, update, upsert=False)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue