Skip to content

Instantly share code, notes, and snippets.

@tipabu

tipabu/flake.py Secret

Last active February 29, 2016 10:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tipabu/e1a03a8486c2fa57b4bc to your computer and use it in GitHub Desktop.
Save tipabu/e1a03a8486c2fa57b4bc to your computer and use it in GitHub Desktop.
Make Swift (or any WSGI app) less reliable
'''
Sometimes, Swift is a little *too* reliable; let's fix that.
With this middleware, operators can throw a bit of chaos at
their users. This may be achieved in two ways:
* Use max_file_size to limit the size of responses. This
doesn't affect any headers (so Content-Length is
unmodified). This can be used to check that clients resume
interrupted downloads. If zero (the default), there is no
limit (ie, this is disabled).
* Use failure_chance to randomly return empty responses
(with status line defined by failure_reason) instead of
passing the request further down the pipeline. This should
be a float in the range [0, 1], with 0 (the default)
meaning "always forward the request" and 1 meaning "always
fail prematurely".
If you are using a non-zero failure_chance, you probably want
this before (to the left of) SLO in your pipeline.
'''
import random
def capped_iter(app_iter, max_len):
'''
Wrap a byte iterator to limit how many bytes it will yield
:param app_iter: the byte iterator to wrap
:param max_len: the maximum number of bytes to yield
'''
def exploding_iter():
'''A generator to do the wrapping/exploding.'''
for chunk in app_iter:
yield chunk[:exploding_iter.to_send]
exploding_iter.to_send -= len(chunk)
if exploding_iter.to_send < 0:
raise Exception('Kaboom!')
exploding_iter.to_send = max_len
return exploding_iter()
def filter_factory(global_conf, **local_conf):
max_file_size = max(int(local_conf.get('max_file_size', '0')), 0)
failure_chance = min(max(float(local_conf.get('failure_chance', '0.0')),
0.0), 1.0)
failure_reason = local_conf.get('failure_reason', '520 Unknown Error')
def filt(app):
def handler(env, start_response):
if random.random() < failure_chance:
start_response(failure_reason, [('Content-Length', '0')])
return ['']
app_iter = app(env, start_response)
if max_file_size > 0:
return capped_iter(app_iter, max_file_size)
return app_iter
return handler
return filt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment