summaryrefslogtreecommitdiffstats
path: root/test/functional
diff options
context:
space:
mode:
authorPrashanth Pai <ppai@redhat.com>2014-03-12 16:54:30 +0530
committerLuis Pabon <lpabon@redhat.com>2014-03-13 05:09:19 -0700
commit2ccd2c4d969cdd6e7feedd21ac2e5cb8498ff37d (patch)
tree1886732cf56e26ab836f9fd67c853eff2005fbeb /test/functional
parent1a6b55714fddf7a1b526a31ee3f27589f01e98e5 (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.py23
-rw-r--r--test/functional/swift_testing.py33
-rwxr-xr-xtest/functional/test_account.py148
-rw-r--r--test/functional/tests.py12
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)