]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: Add access over S3 to multi-tenancy test 8672/head
authorPete Zaitcev <zaitcev@kotori.zaitcev.us>
Tue, 17 May 2016 23:52:04 +0000 (17:52 -0600)
committerPete Zaitcev <zaitcev@kotori.zaitcev.us>
Tue, 17 May 2016 23:52:04 +0000 (17:52 -0600)
Although the main beneficiary of multi-tenancy is Swift, one
can also log in with users that have explicit tenants through S3.
Buckets are namespaced under tenants no matter what protocol
is used to create them. For now, we test the S3 protocol access.

Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
src/test/rgw/test_multen.py

index 8f73712aea4d250c3321d70f4e9505d6cd90a1df..e74396171a6f40ba184cf2558f5027a2df564f34 100644 (file)
@@ -3,7 +3,12 @@
 import json
 import sys
 
+from StringIO import StringIO
+
+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):
@@ -106,7 +111,6 @@ def test4(cluster):
         raise TestException(
             "command: user create --uid %s, returned user_id %s" %
             (tid_uid, outj['user_id']))
-    # XXX maybe add some more checking here for keys and such
 
     # Note that this tests a way to identify a fully-qualified subuser
     # without --tenant and --uid. This is a historic use that we support.
@@ -131,12 +135,224 @@ def test4(cluster):
         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)
+    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)
+    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)
+    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("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("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 != "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 != "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):
@@ -157,6 +373,7 @@ def init(parse_args):
 
     # 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.
@@ -165,13 +382,16 @@ def init(parse_args):
         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