]> git-server-git.apps.pok.os.sepia.ceph.com Git - s3-tests.git/commitdiff
S3 Fuzzer: Start Fuzzing
authorKyle Marsh <kyle.marsh@dreamhost.com>
Fri, 19 Aug 2011 21:54:24 +0000 (14:54 -0700)
committerKyle Marsh <kyle.marsh@dreamhost.com>
Mon, 12 Sep 2011 20:01:07 +0000 (13:01 -0700)
- tweak yaml decision graph
- add test setup bucket creation, etc.
- add output with varying levels of verbosity

request_decision_graph.yml
s3tests/functional/test_fuzzer.py
s3tests/fuzz_headers.py

index e08155cc97e007762b64c98280add2685e38b23c..443b37c876a15e8e7fe392a9fd215e498bf92e3c 100644 (file)
@@ -1,14 +1,14 @@
 start:
     set:
         garbage:
-            - {random 10-3000 printable}
-            - {random 10-1000 binary}
+            - '{random 10-3000 printable}'
+            - '{random 10-1000 binary}'
     choices:
         - bucket
 
 bucket:
     set:
-        urlpath: /{bucket}
+        urlpath: '/{bucket}'
     choices:
         - 13 bucket_get
         - 8 bucket_put
@@ -18,12 +18,12 @@ bucket:
 garbage_method:
     set:
         method:
-            - {random 1-100 printable}
-            - {random 10-100 binary}
+            - '{random 1-100 printable}'
+            - '{random 10-100 binary}'
         urlpath:
-            - /{bucket}
-            - /{bucket}/{object}
-            - {random 10-1000 binary}
+            - '/{bucket}'
+            - '/{bucket}/{object}'
+            - '{random 10-1000 binary}'
     choices:
         - bucket_get_simple
         - bucket_get_filtered
@@ -36,23 +36,23 @@ bucket_delete:
     set:
         method: DELETE
         bucket:
-            - {bucket_writable}
-            - {bucket_not_writable}
-            - 2 {garbage}
+            - '{bucket_writable}'
+            - '{bucket_not_writable}'
+            - '2 {garbage}'
         query:
             - null
             - policy
             - website
-            - 2 {garbage}
+            - '2 {garbage}'
     choices: []
 
 bucket_get:
     set:
         method: GET
         bucket:
-            - {bucket_readable}
-            - {bucket_not_readable}
-            - 2 {garbage}
+            - '{bucket_readable}'
+            - '{bucket_not_readable}'
+            - '2 {garbage}'
     choices:
         - 11 bucket_get_simple
         - bucket_get_filtered
@@ -70,17 +70,17 @@ bucket_get_simple:
             - requestPayment
             - versioning
             - website
-            - 2 {garbage}
+            - '2 {garbage}'
     choices: []
 
 bucket_get_uploads:
     set:
         delimiter:
             - null
-            - 3 'delimiter={garbage}'
+            - '3 delimiter={garbage}'
         prefix:
             - null
-            - 3 'prefix={garbage}'
+            - '3 prefix={garbage}'
         key_marker:
             - null
             - 'key-marker={object_readable}'
@@ -93,12 +93,12 @@ bucket_get_uploads:
             - 'max-uploads={random 1-1000 digits}'
         upload_id_marker:
             - null
-            - 3 'upload-id-marker={random}'
+            - '3 upload-id-marker={random}'
         query:
             - 'uploads'
             - 'uploads&{delimiter}&{prefix}'
             - 'uploads&{max_uploads}&{key_marker}&{upload_id_marker}'
-            - 2 {garbage}
+            - '2 {garbage}'
     choices: []
 
 bucket_get_filtered:
@@ -119,15 +119,15 @@ bucket_get_filtered:
             - null
             - '{delimiter}&{prefix}'
             - '{max-keys}&{marker}'
-            - 2 {garbage}
+            - '2 {garbage}'
     choices: []
 
 bucket_put:
     set:
         bucket:
-            - {bucket_writable}
-            - {bucket_not_writable}
-            - 2 {garbage}
+            - '{bucket_writable}'
+            - '{bucket_not_writable}'
+            - '2 {garbage}'
         method: PUT
     choices:
         - bucket_put_simple
@@ -137,12 +137,12 @@ bucket_put:
 bucket_put_create:
     set:
         body:
-            - 2 {garbage}
+            - '2 {garbage}'
             - '<CreateBucketConfiguration><LocationConstraint>{random 2-10 binary}</LocationConstraint></CreateBucketConfiguration>'
         acl:
             - private
-            - {random 3000 letters}
-            - {random 100-1000 binary}
+            - '{random 3000 letters}'
+            - '{random 100-1000 binary}'
     headers:
         - ['0-1', 'x-amz-acl', '{acl}']
     choices: []
@@ -150,8 +150,8 @@ bucket_put_create:
 bucket_put_versioning:
     set:
         body:
-            - {garbage}
-            - 4 '<VersioningConfiguration>{versioning_status}{mfa_delete_body}</VersioningConfiguration>'
+            - '{garbage}'
+            - '4 <VersioningConfiguration>{versioning_status}{mfa_delete_body}</VersioningConfiguration>'
         mfa_delete_body:
             - null
             - '<Status>{random 2-10 binary}</Status>'
@@ -169,38 +169,38 @@ bucket_put_versioning:
 bucket_put_simple:
     set:
         body:
-            - {acl_body}
-            - {policy_body}
-            - {logging_body}
-            - {notification_body}
-            - {request_payment_body}
-            - {website_body}
+            - '{acl_body}'
+            - '{policy_body}'
+            - '{logging_body}'
+            - '{notification_body}'
+            - '{request_payment_body}'
+            - '{website_body}'
         acl_body:
             - null
             - '<AccessControlPolicy>{owner}{acl}</AccessControlPolicy>'
         owner:
             - null
-            - 7 '<Owner>{id}{display_name}</Owner>'
+            - '7 <Owner>{id}{display_name}</Owner>'
         id:
             - null
             - '<ID>{random 10-200 binary}</ID>'
             - '<ID>{random 1000-3000 printable}</ID>'
         display_name:
             - null
-            - 2 '<DisplayName>{random 10-200 binary}</DisplayName>'
-            - 2 '<DisplayName>{random 1000-3000 printable}</DisplayName>'
-            - 2 '<DisplayName>{random 10-300 letters}@{random 10-300 letters}.{random 2-4 letters}</DisplayName>'
+            - '2 <DisplayName>{random 10-200 binary}</DisplayName>'
+            - '2 <DisplayName>{random 1000-3000 printable}</DisplayName>'
+            - '2 <DisplayName>{random 10-300 letters}@{random 10-300 letters}.{random 2-4 letters}</DisplayName>'
         acl:
             - null
-            - 10 '<AccessControlList><Grant>{grantee}{permission}</Grant></AccessControlList>'
+            - '10 <AccessControlList><Grant>{grantee}{permission}</Grant></AccessControlList>'
         grantee:
             - null
-            - 7 '<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">{id}{display_name}</Grantee>'
+            - '7 <Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">{id}{display_name}</Grantee>'
         permission:
             - null
-            - 7 '<Permission>{permission_value}</Permission>'
+            - '7 <Permission>{permission_value}</Permission>'
         permission_value:
-            - 2 {garbage}
+            - '2 {garbage}'
             - FULL_CONTROL
             - WRITE
             - WRITE_ACP
@@ -208,7 +208,7 @@ bucket_put_simple:
             - READ_ACP
         policy_body:
             - null
-            - 2 {garbage}
+            - '2 {garbage}'
         logging_body:
             - null
             - '<BucketLoggingStatus xmlns="http://doc.s3.amazonaws.com/2006-03-01" />'
@@ -219,31 +219,31 @@ bucket_put_simple:
             - '<TargetPrefix>{random 10-1000 binary}</TargetPrefix>'
         target_grants:
             - null
-            - 10 '<TargetGrants><Grant>{grantee}{permission}</Grant></TargetGrants>'
+            - '10 <TargetGrants><Grant>{grantee}{permission}</Grant></TargetGrants>'
         notification_body:
             - null
             - '<NotificationConfiguration />'
-            - 2 '<NotificationConfiguration><TopicConfiguration>{topic}{event}</TopicConfiguration}</NotificationConfiguration>'
+            - '2 <NotificationConfiguration><TopicConfiguration>{topic}{event}</TopicConfiguration}</NotificationConfiguration>'
         topic:
             - null
-            - 2 '<Topic>{garbage}</Topic>'
+            - '2 <Topic>{garbage}</Topic>'
         event:
             - null
             - '<Event>s3:ReducedRedundancyLostObject</Event>'
-            - 2 '<Event>{garbage}</Event>'
+            - '2 <Event>{garbage}</Event>'
         request_payment_body:
             - null
             - '<RequestPaymentConfiguration xlmns="http://s3.amazonaws.com/doc/2006-03-01/"><Payer>{payer}</Payer></RequestPaymentConfiguration>'
         payer:
             - Requester
             - BucketOwner
-            - 2 {garbage}
+            - '2 {garbage}'
         website_body:
             - null
             - '<WebsiteConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><IndexDocument><Suffix>{suffix}</Suffix><IndexDocument>{error_doc}<WebsiteConfiguration/>'
         suffix:
             - null
-            - 2 {garbage}
+            - '2 {garbage}'
             - '{random 2-10 printable}.html'
         error_doc:
             - null
index 12b19bbeba19d833c169b815a99f9500f3a0dadc..eb2ab1fad70717403def7d5a5f0ba971d3b1bb3c 100644 (file)
@@ -102,6 +102,12 @@ def build_graph():
     return graph
 
 
+#def test_foo():
+    #graph_file = open('request_decision_graph.yml', 'r')
+    #graph = yaml.safe_load(graph_file)
+    #eq(graph['bucket_put_simple']['set']['grantee'], 0)
+
+
 def test_load_graph():
     graph_file = open('request_decision_graph.yml', 'r')
     graph = yaml.safe_load(graph_file)
@@ -257,6 +263,11 @@ def test_expand_recursive_not_too_eager():
     eq(got, 100*'bar')
 
 
+def test_make_choice_unweighted_with_space():
+    prng = random.Random(1)
+    choice = make_choice(['foo bar'], prng)
+    eq(choice, 'foo bar')
+
 def test_weighted_choices():
     graph = build_graph()
     prng = random.Random(1)
@@ -354,7 +365,7 @@ def test_expand_headers():
     decision = descend_graph(graph, 'node1', prng)
     expanded_headers = expand_headers(decision, prng)
 
-    for header, value in expanded_headers:
+    for header, value in expanded_headers.iteritems():
         if header == 'my-header':
             assert_true(value in ['h1', 'h2', 'h3'])
         elif header.startswith('random-header-'):
index 3796fd1919af329592c1b253296fa8a41c2fb7fc..c23ebc6f535554cd09a7d21b525d077eab7c0e39 100644 (file)
@@ -1,4 +1,5 @@
 from boto.s3.connection import S3Connection
+from boto.s3.key import Key
 from optparse import OptionParser
 from boto import UserAgent
 from . import common
@@ -103,11 +104,11 @@ def make_choice(choices, prng):
             continue
         try:
             (weight, value) = option.split(None, 1)
+            weight = int(weight)
         except ValueError:
-            weight = '1'
+            weight = 1
             value = option
 
-        weight = int(weight)
         if value == 'null' or value == 'None':
             value = ''
 
@@ -118,11 +119,11 @@ def make_choice(choices, prng):
 
 
 def expand_headers(decision, prng):
-    expanded_headers = []
+    expanded_headers = {} 
     for header in decision['headers']:
         h = expand(decision, header[0], prng)
         v = expand(decision, header[1], prng)
-        expanded_headers.append([h, v])
+        expanded_headers[h] = v
     return expanded_headers
 
 
@@ -200,6 +201,8 @@ def parse_options():
     parser.add_option('--seed', dest='seed', type='int',  help='initial seed for the random number generator', metavar='SEED')
     parser.add_option('--seed-file', dest='seedfile', help='read seeds for specific requests from FILE', metavar='FILE')
     parser.add_option('-n', dest='num_requests', type='int',  help='issue NUM requests before stopping', metavar='NUM')
+    parser.add_option('-v', '--verbose', dest='verbose', action="store_true",  help='turn on verbose output')
+    parser.add_option('-d', '--debug', dest='debug', action="store_true",  help='turn on debugging (very verbose) output')
     parser.add_option('--decision-graph', dest='graph_filename',  help='file in which to find the request decision graph', metavar='NUM')
 
     parser.set_defaults(num_requests=5)
@@ -215,56 +218,127 @@ def randomlist(seed=None):
         yield rng.random()
 
 
+def populate_buckets(conn, alt):
+    """ Creates buckets and keys for fuzz testing and sets appropriate
+        permissions. Returns a dictionary of the bucket and key names.
+    """
+    breadable = common.get_new_bucket(alt)
+    bwritable = common.get_new_bucket(alt)
+    bnonreadable = common.get_new_bucket(alt)
+
+    oreadable = Key(breadable)
+    owritable = Key(bwritable)
+    ononreadable = Key(breadable)
+    oreadable.set_contents_from_string('oreadable body')
+    owritable.set_contents_from_string('owritable body')
+    ononreadable.set_contents_from_string('ononreadable body')
+
+    breadable.set_acl('public-read')
+    bwritable.set_acl('public-read-write')
+    bnonreadable.set_acl('private')
+    oreadable.set_acl('public-read')
+    owritable.set_acl('public-read-write')
+    ononreadable.set_acl('private')
+
+    return dict(
+        bucket_readable=breadable.name,
+        bucket_writable=bwritable.name,
+        bucket_not_readable=bnonreadable.name,
+        bucket_not_writable=breadable.name,
+        object_readable=oreadable.key,
+        object_writable=owritable.key,
+        object_not_readable=ononreadable.key,
+        object_not_writable=oreadable.key,
+    )
+
+
 def _main():
     """ The main script
     """
     (options, args) = parse_options()
     random.seed(options.seed if options.seed else None)
     s3_connection = common.s3.main
+    alt_connection = common.s3.alt
+
+    if options.outfile:
+        OUT = open(options.outfile, 'w')
+    else:
+        OUT = sys.stderr
+
+    VERBOSE = DEBUG = open('/dev/null', 'w')
+    if options.verbose:
+        VERBOSE = OUT
+    if options.debug:
+        DEBUG = OUT
+        VERBOSE = OUT
 
     request_seeds = None
     if options.seedfile:
         FH = open(options.seedfile, 'r')
-        request_seeds = FH.readlines()
+        request_seeds = [float(line) for line in FH.readlines()]
+        print>>OUT, 'Seedfile: %s' %options.seedfile
+        print>>OUT, 'Number of requests: %d' %len(request_seeds)
     else:
+        if options.seed:
+            print>>OUT, 'Initial Seed: %d' %options.seed
+        print>>OUT, 'Number of requests: %d' %options.num_requests
         random_list = randomlist(options.seed)
         request_seeds = itertools.islice(random_list, options.num_requests)
 
+    print>>OUT, 'Decision Graph: %s' %options.graph_filename
 
     graph_file = open(options.graph_filename, 'r')
     decision_graph = yaml.safe_load(graph_file)
 
-    constants = dict(
-        bucket_readable='TODO-breadable',
-        bucket_not_readable='TODO-bnonreadable',
-        bucket_writable='TODO-bwritable',
-        bucket_not_writable='TODO-bnonwritable',
-        object_readable='TODO-oreadable',
-        object_not_readable='TODO-ononreadable',
-        object_writable='TODO-owritable',
-        object_not_writable='TODO-ononwritable',
-    )
+    constants = populate_buckets(s3_connection, alt_connection)
+    print>>VERBOSE, "Test Buckets/Objects:"
+    for key, value in constants.iteritems():
+        print>>VERBOSE, "\t%s: %s" %(key, value)
 
+    print>>OUT, "Begin Fuzzing..."
+    print>>VERBOSE, '='*80
     for request_seed in request_seeds:
+        print>>OUT, request_seed
+
         prng = random.Random(request_seed)
         decision = assemble_decision(decision_graph, prng)
         decision.update(constants)
 
         method = expand(decision, decision['method'], prng)
-        path = expand(decision, decision['path'], prng)
-        body = expand(decision, decision['body'], prng)
-        headers = expand_headers(decision, prng)
+        path = expand(decision, decision['urlpath'], prng)
 
-        print "Method: %s" % method
-        print "Path: %s" % path
-        print "Headers: %s" % headers
-        print ""
-        print "Body: %s" % body
-        #response = s3_connection.make_request(method, path, data=body, headers=headers, override_num_retries=0)
+        try:
+            body = expand(decision, decision['body'], prng)
+        except KeyError:
+            body = ''
 
+        try:
+            headers = expand_headers(decision, prng)
+        except KeyError:
+            headers = {}
+
+        response = s3_connection.make_request(method, path, data=body, headers=headers, override_num_retries=0)
+
+        print>>VERBOSE, "%s %s" %(method[:100], path[:100])
+        for h, v in headers.iteritems():
+            print>>VERBOSE, "%s: %s" %(h[:50], v[:50])
+        print>>VERBOSE, "%s\n" % body[:100]
+
+        print>>DEBUG, 'FULL REQUEST'
+        print>>DEBUG, 'Method: %r' %method
+        print>>DEBUG, 'Path: %r' %path
+        print>>DEBUG, 'Headers:'
+        for h, v in headers.iteritems():
+            print>>DEBUG, "\t%r: %r" %(h, v)
+        print>>DEBUG, 'Body: %r' %body
+
+        print>>VERBOSE, 'Response status code: %d %s' %(response.status, response.reason)
+        print>>DEBUG, 'Body:\n%s' %response.read()
         if response.status == 500 or response.status == 503:
-            print 'Request generated with seed %d failed:\n%s' % (request_seed, request)
-        pass
+            print>>OUT, 'FAILED:\n%s' %request
+        print>>VERBOSE, '='*80
+    print>>OUT, '...done fuzzing'
+    common.teardown()
 
 
 def main():