diff options
Diffstat (limited to 'src/test/rgw/test_multen.py')
-rw-r--r-- | src/test/rgw/test_multen.py | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/src/test/rgw/test_multen.py b/src/test/rgw/test_multen.py new file mode 100644 index 00000000..91464d33 --- /dev/null +++ b/src/test/rgw/test_multen.py @@ -0,0 +1,400 @@ +# Test of mult-tenancy + +import json +import sys + +from boto.s3.connection import S3Connection, OrdinaryCallingFormat + +# XXX once we're done, break out the common code into a library module +# See https://github.com/ceph/ceph/pull/8646 +import test_multi as t + +class TestException(Exception): + pass + +# +# Create a traditional user, S3-only, global (empty) tenant +# +def test2(cluster): + uid = "tester2" + display_name = "'Test User 2'" + access_key = "tester2KEY" + s3_secret = "test3pass" + cmd = t.build_cmd('--uid', uid, + '--display-name', display_name, + '--access-key', access_key, + '--secret', s3_secret, + "user create") + out, ret = cluster.rgw_admin(cmd, check_retcode=False) + if ret != 0: + raise TestException("failed command: user create --uid %s" % uid) + + try: + outj = json.loads(out.decode('utf-8')) + except ValueError: + raise TestException("invalid json after: user create --uid %s" % uid) + if not isinstance(outj, dict): + raise TestException("bad json after: user create --uid %s" % uid) + if outj['user_id'] != uid: + raise TestException( + "command: user create --uid %s, returned user_id %s" % + (uid, outj['user_id'])) + +# +# Create a tenantized user with --tenant foo +# +def test3(cluster): + tid = "testx3" + uid = "tester3" + display_name = "Test_User_3" + access_key = "tester3KEY" + s3_secret = "test3pass" + cmd = t.build_cmd( + '--tenant', tid, + '--uid', uid, + '--display-name', display_name, + '--access-key', access_key, + '--secret', s3_secret, + "user create") + out, ret = cluster.rgw_admin(cmd, check_retcode=False) + if ret != 0: + raise TestException("failed command: user create --uid %s" % uid) + + try: + outj = json.loads(out.decode('utf-8')) + except ValueError: + raise TestException("invalid json after: user create --uid %s" % uid) + if not isinstance(outj, dict): + raise TestException("bad json after: user create --uid %s" % uid) + tid_uid = "%s$%s" % (tid, uid) + if outj['user_id'] != tid_uid: + raise TestException( + "command: user create --uid %s, returned user_id %s" % + (tid_uid, outj['user_id'])) + +# +# Create a tenantized user with a subuser +# +# N.B. The aim of this test is not just to create a subuser, but to create +# the key with a separate command, which does not use --tenant, but extracts +# the tenant from the subuser. No idea why we allow this. There was some kind +# of old script that did this. +# +def test4(cluster): + tid = "testx4" + uid = "tester4" + subid = "test4" + + display_name = "Test_User_4" + cmd = t.build_cmd( + '--tenant', tid, + '--uid', uid, + '--display-name', display_name, + '--subuser', '%s:%s' % (uid, subid), + '--key-type', 'swift', + '--access', 'full', + "user create") + out, ret = cluster.rgw_admin(cmd, check_retcode=False) + if ret != 0: + raise TestException("failed command: user create --uid %s" % uid) + + try: + outj = json.loads(out.decode('utf-8')) + except ValueError: + raise TestException("invalid json after: user create --uid %s" % uid) + if not isinstance(outj, dict): + raise TestException("bad json after: user create --uid %s" % uid) + tid_uid = "%s$%s" % (tid, uid) + if outj['user_id'] != tid_uid: + raise TestException( + "command: user create --uid %s, returned user_id %s" % + (tid_uid, outj['user_id'])) + + # Note that this tests a way to identify a fully-qualified subuser + # without --tenant and --uid. This is a historic use that we support. + swift_secret = "test3pass" + cmd = t.build_cmd( + '--subuser', "'%s$%s:%s'" % (tid, uid, subid), + '--key-type', 'swift', + '--secret', swift_secret, + "key create") + out, ret = cluster.rgw_admin(cmd, check_retcode=False) + if ret != 0: + raise TestException("failed command: key create --uid %s" % uid) + + try: + outj = json.loads(out.decode('utf-8')) + except ValueError: + raise TestException("invalid json after: key create --uid %s" % uid) + if not isinstance(outj, dict): + raise TestException("bad json after: key create --uid %s" % uid) + tid_uid = "%s$%s" % (tid, uid) + if outj['user_id'] != tid_uid: + raise TestException( + "command: key create --uid %s, returned user_id %s" % + (tid_uid, outj['user_id'])) + # These tests easily can throw KeyError, needs a try: XXX + skj = outj['swift_keys'][0] + if skj['secret_key'] != swift_secret: + raise TestException( + "command: key create --uid %s, returned swift key %s" % + (tid_uid, skj['secret_key'])) + +# +# Access the cluster, create containers in two tenants, verify it all works. +# + +def test5_add_s3_key(cluster, tid, uid): + secret = "%spass" % uid + if tid: + tid_uid = "%s$%s" % (tid, uid) + else: + tid_uid = uid + + cmd = t.build_cmd( + '--uid', "'%s'" % (tid_uid,), + '--access-key', uid, + '--secret', secret, + "key create") + out, ret = cluster.rgw_admin(cmd, check_retcode=False) + if ret != 0: + raise TestException("failed command: key create --uid %s" % uid) + + try: + outj = json.loads(out.decode('utf-8')) + except ValueError: + raise TestException("invalid json after: key create --uid %s" % uid) + if not isinstance(outj, dict): + raise TestException("bad json after: key create --uid %s" % uid) + if outj['user_id'] != tid_uid: + raise TestException( + "command: key create --uid %s, returned user_id %s" % + (uid, outj['user_id'])) + skj = outj['keys'][0] + if skj['secret_key'] != secret: + raise TestException( + "command: key create --uid %s, returned s3 key %s" % + (uid, skj['secret_key'])) + +def test5_add_swift_key(cluster, tid, uid, subid): + secret = "%spass" % uid + if tid: + tid_uid = "%s$%s" % (tid, uid) + else: + tid_uid = uid + + cmd = t.build_cmd( + '--subuser', "'%s:%s'" % (tid_uid, subid), + '--key-type', 'swift', + '--secret', secret, + "key create") + out, ret = cluster.rgw_admin(cmd, check_retcode=False) + if ret != 0: + raise TestException("failed command: key create --uid %s" % uid) + + try: + outj = json.loads(out.decode('utf-8')) + except ValueError: + raise TestException("invalid json after: key create --uid %s" % uid) + if not isinstance(outj, dict): + raise TestException("bad json after: key create --uid %s" % uid) + if outj['user_id'] != tid_uid: + raise TestException( + "command: key create --uid %s, returned user_id %s" % + (uid, outj['user_id'])) + # XXX checking wrong thing here (S3 key) + skj = outj['keys'][0] + if skj['secret_key'] != secret: + raise TestException( + "command: key create --uid %s, returned s3 key %s" % + (uid, skj['secret_key'])) + +def test5_make_user(cluster, tid, uid, subid): + """ + :param tid: Tenant ID string or None for the legacy tenant + :param uid: User ID string + :param subid: Subuser ID, may be None for S3-only users + """ + display_name = "'Test User %s'" % uid + + cmd = "" + if tid: + cmd = t.build_cmd(cmd, + '--tenant', tid) + cmd = t.build_cmd(cmd, + '--uid', uid, + '--display-name', display_name) + if subid: + cmd = t.build_cmd(cmd, + '--subuser', '%s:%s' % (uid, subid), + '--key-type', 'swift') + cmd = t.build_cmd(cmd, + '--access', 'full', + "user create") + + out, ret = cluster.rgw_admin(cmd, check_retcode=False) + if ret != 0: + raise TestException("failed command: user create --uid %s" % uid) + try: + outj = json.loads(out.decode('utf-8')) + except ValueError: + raise TestException("invalid json after: user create --uid %s" % uid) + if not isinstance(outj, dict): + raise TestException("bad json after: user create --uid %s" % uid) + if tid: + tid_uid = "%s$%s" % (tid, uid) + else: + tid_uid = uid + if outj['user_id'] != tid_uid: + raise TestException( + "command: user create --uid %s, returned user_id %s" % + (tid_uid, outj['user_id'])) + + # + # For now, this uses hardcoded passwords based on uid. + # They are all different for ease of debugging in case something crosses. + # + test5_add_s3_key(cluster, tid, uid) + if subid: + test5_add_swift_key(cluster, tid, uid, subid) + +def test5_poke_s3(cluster): + + bucketname = "test5cont1" + objname = "obj1" + + # Not sure if we like useless information printed, but the rest of the + # test framework is insanely talkative when it executes commands. + # So, to keep it in line and have a marker when things go wrong, this. + print("PUT bucket %s object %s for tenant A (empty)" % + (bucketname, objname)) + c = S3Connection( + aws_access_key_id="tester5a", + aws_secret_access_key="tester5apass", + is_secure=False, + host="localhost", + port = cluster.port, + calling_format = OrdinaryCallingFormat()) + + bucket = c.create_bucket(bucketname) + + key = bucket.new_key(objname) + headers = { "Content-Type": "text/plain" } + key.set_contents_from_string(b"Test5A\n", headers) + key.set_acl('public-read') + + # + # Now it's getting interesting. We're logging into a tenantized user. + # + print("PUT bucket %s object %s for tenant B" % (bucketname, objname)) + c = S3Connection( + aws_access_key_id="tester5b1", + aws_secret_access_key="tester5b1pass", + is_secure=False, + host="localhost", + port = cluster.port, + calling_format = OrdinaryCallingFormat()) + + bucket = c.create_bucket(bucketname) + bucket.set_canned_acl('public-read') + + key = bucket.new_key(objname) + headers = { "Content-Type": "text/plain" } + key.set_contents_from_string(b"Test5B\n", headers) + key.set_acl('public-read') + + # + # Finally, let's fetch a couple of objects and verify that they + # are what they should be and we didn't get them overwritten. + # Note that we access one of objects across tenants using the colon. + # + print("GET bucket %s object %s for tenants A and B" % + (bucketname, objname)) + c = S3Connection( + aws_access_key_id="tester5a", + aws_secret_access_key="tester5apass", + is_secure=False, + host="localhost", + port = cluster.port, + calling_format = OrdinaryCallingFormat()) + + bucket = c.get_bucket(bucketname) + + key = bucket.get_key(objname) + body = key.get_contents_as_string() + if body != b"Test5A\n": + raise TestException("failed body check, bucket %s object %s" % + (bucketname, objname)) + + bucket = c.get_bucket("test5b:"+bucketname) + key = bucket.get_key(objname) + body = key.get_contents_as_string() + if body != b"Test5B\n": + raise TestException( + "failed body check, tenant %s bucket %s object %s" % + ("test5b", bucketname, objname)) + + print("Poke OK") + + +def test5(cluster): + # Plan: + # 0. create users tester5a and test5b$tester5b1 test5b$tester5b2 + # 1. create buckets "test5cont" under test5a and test5b + # 2. create objects in the buckets + # 3. access objects (across users in container test5b) + + test5_make_user(cluster, None, "tester5a", "test5a") + test5_make_user(cluster, "test5b", "tester5b1", "test5b1") + test5_make_user(cluster, "test5b", "tester5b2", "test5b2") + + test5_poke_s3(cluster) + + +# XXX this parse_args boolean makes no sense. we should pass argv[] instead, +# possibly empty. (copied from test_multi, correct it there too) +def init(parse_args): + + #argv = [] + #if parse_args: + # argv = sys.argv[1:] + #args = parser.parse_args(argv) + + #rgw_multi = RGWMulti(int(args.num_zones)) + #rgw_multi.setup(not args.no_bootstrap) + + # __init__(): + port = 8001 + clnum = 1 # number of clusters + clid = 1 # 1-based + cluster = t.RGWCluster(clid, port) + + # setup(): + cluster.start() + cluster.start_rgw() + + # The cluster is always reset at this point, so we don't need to list + # users or delete pre-existing users. + + try: + test2(cluster) + test3(cluster) + test4(cluster) + test5(cluster) + except TestException as e: + cluster.stop_rgw() + cluster.stop() + sys.stderr.write("FAIL\n") + sys.stderr.write("%s\n" % str(e)) + return 1 + + # teardown(): + cluster.stop_rgw() + cluster.stop() + return 0 + +def setup_module(): + return init(False) + +if __name__ == "__main__": + sys.exit(init(True)) |