diff options
| author | Prashanth Pai <ppai@redhat.com> | 2014-03-12 16:54:30 +0530 | 
|---|---|---|
| committer | Luis Pabon <lpabon@redhat.com> | 2014-03-13 05:09:19 -0700 | 
| commit | 2ccd2c4d969cdd6e7feedd21ac2e5cb8498ff37d (patch) | |
| tree | 1886732cf56e26ab836f9fd67c853eff2005fbeb /test/functional | |
| parent | 1a6b55714fddf7a1b526a31ee3f27589f01e98e5 (diff) | |
Sync with OpenStack Swift v1.13.0
Also, bumped version of gluster-swift to v1.13.0
Change-Id: I797dc704c9523540cba847b1e8ff3da97b79630c
Signed-off-by: Prashanth Pai <ppai@redhat.com>
Reviewed-on: http://review.gluster.org/7229
Reviewed-by: Chetan Risbud <crisbud@redhat.com>
Reviewed-by: Luis Pabon <lpabon@redhat.com>
Tested-by: Luis Pabon <lpabon@redhat.com>
Diffstat (limited to 'test/functional')
| -rw-r--r-- | test/functional/swift_test_client.py | 23 | ||||
| -rw-r--r-- | test/functional/swift_testing.py | 33 | ||||
| -rwxr-xr-x | test/functional/test_account.py | 148 | ||||
| -rw-r--r-- | test/functional/tests.py | 12 | 
4 files changed, 192 insertions, 24 deletions
diff --git a/test/functional/swift_test_client.py b/test/functional/swift_test_client.py index 47e023e..ecb2a6d 100644 --- a/test/functional/swift_test_client.py +++ b/test/functional/swift_test_client.py @@ -40,7 +40,7 @@ class RequestError(Exception):  class ResponseError(Exception): -    def __init__(self, response, method, path): +    def __init__(self, response, method=None, path=None):          self.status = response.status          self.reason = response.reason          self.method = method @@ -310,10 +310,11 @@ class Base:      def __str__(self):          return self.name -    def header_fields(self, fields): +    def header_fields(self, required_fields, optional_fields=()):          headers = dict(self.conn.response.getheaders())          ret = {} -        for field in fields: + +        for field in required_fields:              if field[1] not in headers:                  raise ValueError("%s was not found in response header" %                                   (field[1])) @@ -322,6 +323,15 @@ class Base:                  ret[field[0]] = int(headers[field[1]])              except ValueError:                  ret[field[0]] = headers[field[1]] + +        for field in optional_fields: +            if field[1] not in headers: +                continue +            try: +                ret[field[0]] = int(headers[field[1]]) +            except ValueError: +                ret[field[0]] = headers[field[1]] +          return ret @@ -480,10 +490,11 @@ class Container(Base):                                 parms=parms, cfg=cfg)          if self.conn.response.status == 204: -            fields = [['bytes_used', 'x-container-bytes-used'], -                      ['object_count', 'x-container-object-count']] +            required_fields = [['bytes_used', 'x-container-bytes-used'], +                               ['object_count', 'x-container-object-count']] +            optional_fields = [['versions', 'x-versions-location']] -            return self.header_fields(fields) +            return self.header_fields(required_fields, optional_fields)          raise ResponseError(self.conn.response, 'HEAD',                              self.conn.make_path(self.path)) diff --git a/test/functional/swift_testing.py b/test/functional/swift_testing.py index b5642b3..f05cb48 100644 --- a/test/functional/swift_testing.py +++ b/test/functional/swift_testing.py @@ -18,6 +18,7 @@ import os  import socket  import sys  from time import sleep +from urlparse import urlparse  from test import get_config @@ -119,18 +120,23 @@ conn = [None, None, None]  def retry(func, *args, **kwargs):      """ -    You can use the kwargs to override the 'retries' (default: 5) and -    'use_account' (default: 1). +    You can use the kwargs to override: +      'retries' (default: 5) +      'use_account' (default: 1) - which user's token to pass +      'url_account' (default: matches 'use_account') - which user's storage URL +      'resource' (default: url[url_account] - URL to connect to; retry() +          will interpolate the variable :storage_url: if present      """      global url, token, parsed, conn      retries = kwargs.get('retries', 5) -    use_account = 1 -    if 'use_account' in kwargs: -        use_account = kwargs['use_account'] -        del kwargs['use_account'] -    use_account -= 1 -    attempts = 0 -    backoff = 1 +    attempts, backoff = 0, 1 + +    # use account #1 by default; turn user's 1-indexed account into 0-indexed +    use_account = kwargs.pop('use_account', 1) - 1 + +    # access our own account by default +    url_account = kwargs.pop('url_account', use_account + 1) - 1 +      while attempts <= retries:          attempts += 1          try: @@ -146,8 +152,13 @@ def retry(func, *args, **kwargs):              if not parsed[use_account] or not conn[use_account]:                  parsed[use_account], conn[use_account] = \                      http_connection(url[use_account]) -            return func(url[use_account], token[use_account], -                        parsed[use_account], conn[use_account], + +            # default resource is the account url[url_account] +            resource = kwargs.pop('resource', '%(storage_url)s') +            template_vars = {'storage_url': url[url_account]} +            parsed_result = urlparse(resource % template_vars) +            return func(url[url_account], token[use_account], +                        parsed_result, conn[url_account],                          *args, **kwargs)          except (socket.error, HTTPException):              if attempts > retries: diff --git a/test/functional/test_account.py b/test/functional/test_account.py index b2f743f..d456090 100755 --- a/test/functional/test_account.py +++ b/test/functional/test_account.py @@ -16,12 +16,16 @@  # limitations under the License.  import unittest +import json  from nose import SkipTest  from swift.common.constraints import MAX_META_COUNT, MAX_META_NAME_LENGTH, \      MAX_META_OVERALL_SIZE, MAX_META_VALUE_LENGTH - +from swift.common.middleware.acl import format_acl +from test.functional.swift_test_client import Connection +from test import get_config  from swift_testing import check_response, retry, skip, web_front_end +import swift_testing  class TestAccount(unittest.TestCase): @@ -66,6 +70,148 @@ class TestAccount(unittest.TestCase):          self.assert_(resp.status in (200, 204), resp.status)          self.assertEquals(resp.getheader('x-account-meta-test'), 'Value') +    def test_tempauth_account_acls(self): +        if skip: +            raise SkipTest + +        # Determine whether this cluster has account ACLs; if not, skip test +        conn = Connection(get_config('func_test')) +        conn.authenticate() +        status = conn.make_request( +            'GET', '/info', cfg={'verbatim_path': True}) +        if status // 100 != 2: +            # Can't tell if account ACLs are enabled; skip tests proactively. +            raise SkipTest +        else: +            cluster_info = json.loads(conn.response.read()) +            if not cluster_info.get('tempauth', {}).get('account_acls'): +                raise SkipTest +            if 'keystoneauth' in cluster_info: +                # Unfortunate hack -- tempauth (with account ACLs) is expected +                # to play nice with Keystone (without account ACLs), but Zuul +                # functest framework doesn't give us an easy way to get a +                # tempauth user. +                raise SkipTest + +        def post(url, token, parsed, conn, headers): +            new_headers = dict({'X-Auth-Token': token}, **headers) +            conn.request('POST', parsed.path, '', new_headers) +            return check_response(conn) + +        def put(url, token, parsed, conn, headers): +            new_headers = dict({'X-Auth-Token': token}, **headers) +            conn.request('PUT', parsed.path, '', new_headers) +            return check_response(conn) + +        def delete(url, token, parsed, conn, headers): +            new_headers = dict({'X-Auth-Token': token}, **headers) +            conn.request('DELETE', parsed.path, '', new_headers) +            return check_response(conn) + +        def head(url, token, parsed, conn): +            conn.request('HEAD', parsed.path, '', {'X-Auth-Token': token}) +            return check_response(conn) + +        def get(url, token, parsed, conn): +            conn.request('GET', parsed.path, '', {'X-Auth-Token': token}) +            return check_response(conn) + +        try: +            # User1 can POST to their own account (and reset the ACLs) +            resp = retry(post, headers={'X-Account-Access-Control': '{}'}, +                         use_account=1) +            resp.read() +            self.assertEqual(resp.status, 204) +            self.assertEqual(resp.getheader('X-Account-Access-Control'), None) + +            # User1 can GET their own empty account +            resp = retry(get, use_account=1) +            resp.read() +            self.assertEqual(resp.status // 100, 2) +            self.assertEqual(resp.getheader('X-Account-Access-Control'), None) + +            # User2 can't GET User1's account +            resp = retry(get, use_account=2, url_account=1) +            resp.read() +            self.assertEqual(resp.status, 403) + +            # User1 is swift_owner of their own account, so they can POST an +            # ACL -- let's do this and make User2 (test_user[1]) an admin +            acl_user = swift_testing.swift_test_user[1] +            acl = {'admin': [acl_user]} +            headers = {'x-account-access-control': format_acl( +                version=2, acl_dict=acl)} +            resp = retry(post, headers=headers, use_account=1) +            resp.read() +            self.assertEqual(resp.status, 204) + +            # User1 can see the new header +            resp = retry(get, use_account=1) +            resp.read() +            self.assertEqual(resp.status // 100, 2) +            data_from_headers = resp.getheader('x-account-access-control') +            expected = json.dumps(acl, separators=(',', ':')) +            self.assertEqual(data_from_headers, expected) + +            # Now User2 should be able to GET the account and see the ACL +            resp = retry(head, use_account=2, url_account=1) +            resp.read() +            data_from_headers = resp.getheader('x-account-access-control') +            self.assertEqual(data_from_headers, expected) + +            # Revoke User2's admin access, grant User2 read-write access +            acl = {'read-write': [acl_user]} +            headers = {'x-account-access-control': format_acl( +                version=2, acl_dict=acl)} +            resp = retry(post, headers=headers, use_account=1) +            resp.read() +            self.assertEqual(resp.status, 204) + +            # User2 can still GET the account, but not see the ACL +            # (since it's privileged data) +            resp = retry(head, use_account=2, url_account=1) +            resp.read() +            self.assertEqual(resp.status, 204) +            self.assertEqual(resp.getheader('x-account-access-control'), None) + +            # User2 can PUT and DELETE a container +            resp = retry(put, use_account=2, url_account=1, +                         resource='%(storage_url)s/mycontainer', headers={}) +            resp.read() +            self.assertEqual(resp.status, 201) +            resp = retry(delete, use_account=2, url_account=1, +                         resource='%(storage_url)s/mycontainer', headers={}) +            resp.read() +            self.assertEqual(resp.status, 204) + +            # Revoke User2's read-write access, grant User2 read-only access +            acl = {'read-only': [acl_user]} +            headers = {'x-account-access-control': format_acl( +                version=2, acl_dict=acl)} +            resp = retry(post, headers=headers, use_account=1) +            resp.read() +            self.assertEqual(resp.status, 204) + +            # User2 can still GET the account, but not see the ACL +            # (since it's privileged data) +            resp = retry(head, use_account=2, url_account=1) +            resp.read() +            self.assertEqual(resp.status, 204) +            self.assertEqual(resp.getheader('x-account-access-control'), None) + +            # User2 can't PUT a container +            resp = retry(put, use_account=2, url_account=1, +                         resource='%(storage_url)s/mycontainer', headers={}) +            resp.read() +            self.assertEqual(resp.status, 403) + +        finally: +            # Make sure to clean up even if tests fail -- User2 should not +            # have access to User1's account in other functional tests! +            resp = retry(post, headers={'X-Account-Access-Control': '{}'}, +                         use_account=1) +            resp.read() +      def test_unicode_metadata(self):          if skip:              raise SkipTest diff --git a/test/functional/tests.py b/test/functional/tests.py index 9469cff..0d9a9ef 100644 --- a/test/functional/tests.py +++ b/test/functional/tests.py @@ -149,7 +149,7 @@ def timeout(seconds, method, *args, **kwargs):      return False -class Utils: +class Utils(object):      @classmethod      def create_ascii_name(cls, length=None):          return uuid.uuid4().hex @@ -201,7 +201,7 @@ class Base2(object):          Utils.create_name = Utils.create_ascii_name -class TestAccountEnv: +class TestAccountEnv(object):      @classmethod      def setUp(cls):          cls.conn = Connection(config) @@ -388,7 +388,7 @@ class TestAccountUTF8(Base2, TestAccount):      set_up = False -class TestAccountNoContainersEnv: +class TestAccountNoContainersEnv(object):      @classmethod      def setUp(cls):          cls.conn = Connection(config) @@ -417,7 +417,7 @@ class TestAccountNoContainersUTF8(Base2, TestAccountNoContainers):      set_up = False -class TestContainerEnv: +class TestContainerEnv(object):      @classmethod      def setUp(cls):          cls.conn = Connection(config) @@ -708,7 +708,7 @@ class TestContainerUTF8(Base2, TestContainer):      set_up = False -class TestContainerPathsEnv: +class TestContainerPathsEnv(object):      @classmethod      def setUp(cls):          raise SkipTest('Objects ending in / are not supported') @@ -888,7 +888,7 @@ class TestContainerPaths(Base):                         ['dir1/subdir with spaces/file B']) -class TestFileEnv: +class TestFileEnv(object):      @classmethod      def setUp(cls):          cls.conn = Connection(config)  | 
