Skip to content

Commit

Permalink
added requirements, readme and .gitignore
Browse files Browse the repository at this point in the history
  • Loading branch information
neyer committed Nov 9, 2012
1 parent 0eaec19 commit eb19866
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 16 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.pyc
darkside-mismatch.log
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
dark side
============
a response-comparing proxy server made of pure evil

you can use it to test out a new apprentice, to make sure it responds to all queries the same way master does.

##setup

`pip install -r requirements.txt`

##usage

suppose you have an existing api at api.legitimate-business.com, and you want to test the new api, at new-api.legitimate-business.com. run this:

`python server.py api.legitimate-business.com --apprentice new-api.legitimate-business.com`

darkside will listen on port 8987 (change that with --port)

when you request /some/resource, darkside will get fetch both api.legitimate-business.com/some/resource and new-api.legitimate-business.com/some/resource, and compare the two. if they are different, darkside will make an entry in darkside-mismatch.log as well as print to the console.

there's no limit to the number of apprentices you can take on - but don't go crazy, those guys get annoying when they're all "master master, please tell me more divine secrets" all the time.



7 changes: 7 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
argparse==1.2.1
colorama==0.2.4
gevent==0.13.8
greenlet==0.4.0
json-tools==0.1.30
requests==0.14.2
wsgiref==0.1.2
58 changes: 42 additions & 16 deletions server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@
import json
import argparse
import sys
import logging
import json_tools
from gevent.pywsgi import WSGIHandler, WSGIServer

class Handler(object):

def __init__(self,hosts):
self._hosts = hosts
def __init__(self,master,apprentices):
self._master = master
self._apprentices = apprentices

@property
def downstream_servers(self):
return self._hosts
self._mismatch_log = logging.getLogger("darkside-mismatch")
self._mismatch_log.setLevel(logging.INFO)
self._mismatch_log.addHandler(logging.FileHandler('darkside-mismatch.log'))

def __call__(self,environ,start_response):

Expand All @@ -21,7 +24,11 @@ def __call__(self,environ,start_response):
method = environ['REQUEST_METHOD']

all_bodies = []
for server in self.downstream_servers:
all_servers = [ self._master] + self._apprentices
master_response = None
master_body = None

for server in all_servers:
req = self.make_request(environ, server)
req.send()
this_body = req.response.content
Expand All @@ -30,15 +37,20 @@ def __call__(self,environ,start_response):
except ValueError, e:
this_response = this_body

for other_body in all_bodies:
if not this_response == other_body:
print '\noh snap, mismatch in two bodies!\n: %s?%s\n'% (path, query_string)
if server == self._master:
master_response = req.response
master_body = this_response

if not this_response == master_body:
self._mismatch_log.info('apprentice %s failed fetching %s?%s' % (server, path,query_string))
if isinstance(this_response, dict) and isinstance(master_body, dict):
diff = json_tools.diff(this_response,master_body)
all_bodies.append(this_response)

if all_bodies:
status = '%s %s' % (req.response.status_code, req.response.reason)
start_response(status,req.response.headers.items())
return this_body
if master_response:
status = '%s %s' % (master_response.status_code, master_response.reason)
start_response(status,master_response.headers.items())
return master_response.content
else:
start_response('200 OK',[])
return 'fudgesickles'
Expand All @@ -49,9 +61,23 @@ def make_request(self, environ, for_server):
params=environ['QUERY_STRING'])

def main():
parser = argparse.ArgumentParser(description='compare multiple server\'s responses')
parser.add_argument('--host',action='append',dest='hosts',help='include this host in the comparison')
#set up the arguments parser
parser = argparse.ArgumentParser(description='run a proxy server that compares response from the master to responses returned by any number of apprentices')
parser.add_argument('master',help="return this server's response")
parser.add_argument('--apprentice',action='append',dest='apprentices',
help="compare this host's response to the master's. use this argument multiple times for multiple apprentices.")
parser.add_argument('--port',help='listen on this port',type=int,default=8987)
#now parse that shit
args = parser.parse_args()
WSGIServer(('127.0.0.1',8888),Handler(args.hosts)).serve_forever()

#set up the logger
logging.basicConfig()

print 'dark side: listening on port %s for your deepest fears and worries' % args.port
print 'master:'
print '\t',args.master
print 'apprentices:'
print '\t'+(' '.join(args.apprentices))
WSGIServer(('127.0.0.1',args.port),Handler(args.master, args.apprentices)).serve_forever()

if __name__ == "__main__" : main()

0 comments on commit eb19866

Please sign in to comment.