Draft: s3acl decorator

Current swift3 has a couple of ways for acl handling.
To simplify test code, this introduces s3acl decorator
which suggests which way the test method will check.

In default, swift3 do the native acl testing.
If needed, we can add the s3acl decorater and an argument
like as follows:

No decorator -> test only native acl
@s3acl -> test both of native acl and s3acl
@s3acl(s3acl_only=True) -> test only s3acl

This is still a draft version and most of tests should
be fixed.

TODO:
- keeping sysmeta for s3acl when the test override
  backend swift by register.

Change-Id: I17a423b5c655a670a4304d47ff927df28ac0a37f
This commit is contained in:
Kota Tsuyuzaki 2014-12-05 11:11:38 -08:00
parent 8e1e7152f9
commit 7fd58f9bfd
4 changed files with 110 additions and 0 deletions

View File

@ -0,0 +1,18 @@
# Copyright (c) 2013 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class NotMethodException(Exception):
pass

View File

@ -21,6 +21,7 @@ from swift.common.swob import Request
from swift3.test.unit import Swift3TestCase
from swift3.etree import Element, SubElement, fromstring, tostring
from swift3.test.unit.test_s3_acl import s3acl
class TestSwift3Bucket(Swift3TestCase):
@ -211,6 +212,7 @@ class TestSwift3Bucket(Swift3TestCase):
code = self._test_method_error('PUT', '/bucket', swob.HTTPServerError)
self.assertEquals(code, 'InternalError')
@s3acl
def test_bucket_PUT(self):
req = Request.blank('/bucket',
environ={'REQUEST_METHOD': 'PUT'},
@ -219,6 +221,7 @@ class TestSwift3Bucket(Swift3TestCase):
self.assertEquals(status.split()[0], '200')
self.assertEquals(headers['Location'], '/bucket')
@s3acl
def test_bucket_PUT_with_location(self):
elem = Element('CreateBucketConfiguration')
SubElement(elem, 'LocationConstraint').text = 'US'
@ -242,6 +245,7 @@ class TestSwift3Bucket(Swift3TestCase):
self.assertTrue('X-Container-Read' in headers)
self.assertEquals(headers.get('X-Container-Read'), '.r:*,.rlistings')
@s3acl
def test_bucket_PUT_with_location_error(self):
elem = Element('CreateBucketConfiguration')
SubElement(elem, 'LocationConstraint').text = 'XXX'
@ -255,6 +259,7 @@ class TestSwift3Bucket(Swift3TestCase):
self.assertEquals(self._get_error_code(body),
'InvalidLocationConstraint')
@s3acl
def test_bucket_PUT_with_location_invalid_xml(self):
req = Request.blank('/bucket',
environ={'REQUEST_METHOD': 'PUT'},
@ -277,6 +282,7 @@ class TestSwift3Bucket(Swift3TestCase):
swob.HTTPServerError)
self.assertEquals(code, 'InternalError')
@s3acl
def test_bucket_DELETE(self):
req = Request.blank('/bucket',
environ={'REQUEST_METHOD': 'DELETE'},

View File

@ -21,6 +21,7 @@ from swift.common import swob
from swift.common.swob import Request
from swift3.test.unit import Swift3TestCase
from swift3.test.unit.test_s3_acl import s3acl
class TestSwift3Obj(Swift3TestCase):
@ -119,9 +120,11 @@ class TestSwift3Obj(Swift3TestCase):
swob.HTTPServiceUnavailable)
self.assertEquals(code, 'InternalError')
@s3acl
def test_object_GET(self):
self._test_object_GETorHEAD('GET')
@s3acl
def test_object_GET_Range(self):
req = Request.blank('/bucket/object',
environ={'REQUEST_METHOD': 'GET'},
@ -138,6 +141,7 @@ class TestSwift3Obj(Swift3TestCase):
swob.HTTPRequestedRangeNotSatisfiable)
self.assertEquals(code, 'InvalidRange')
@s3acl
def test_object_GET_Response(self):
req = Request.blank('/bucket/object',
environ={'REQUEST_METHOD': 'GET',
@ -197,6 +201,7 @@ class TestSwift3Obj(Swift3TestCase):
swob.HTTPServiceUnavailable)
self.assertEquals(code, 'InternalError')
@s3acl
def test_object_PUT(self):
etag = self.response_headers['etag']
content_md5 = etag.decode('hex').encode('base64').strip()
@ -264,6 +269,7 @@ class TestSwift3Obj(Swift3TestCase):
swob.HTTPServiceUnavailable)
self.assertEquals(code, 'InternalError')
@s3acl
def test_object_DELETE(self):
req = Request.blank('/bucket/object',
environ={'REQUEST_METHOD': 'DELETE'},

View File

@ -15,6 +15,10 @@
import unittest
import simplejson as json
import functools
import sys
import traceback
from mock import patch, MagicMock
from swift.common import swob
from swift.common.swob import Request
@ -24,10 +28,50 @@ from swift3.subresource import ACL, ACLPrivate, User, encode_acl, \
AuthenticatedUsers, AllUsers, Owner, Grant, PERMISSIONS
from swift3.test.unit.test_middleware import Swift3TestCase
from swift3.cfg import CONF
from swift3.test.unit.exceptions import NotMethodException
XMLNS_XSI = 'http://www.w3.org/2001/XMLSchema-instance'
def s3acl(func=None, s3acl_only=False):
"""
NOTE: s3acl decorator needs an instance of swift3 testing framework.
(i.e. An instance for first argument is necessary)
"""
if func is None:
return functools.partial(s3acl, s3acl_only=s3acl_only)
@functools.wraps(func)
def s3acl_decorator(*args, **kwargs):
if not args and not kwargs:
raise NotMethodException('Use s3acl decorator for a method')
def call_func(failing_point=''):
try:
func(*args, **kwargs)
except AssertionError:
# Make traceback message to clarify the assertion
exc_type, exc_instance, exc_traceback = sys.exc_info()
formatted_traceback = ''.join(traceback.format_tb(
exc_traceback))
message = '\n%s\n%s:\n%s' % (formatted_traceback,
exc_type.__name__,
exc_instance.message)
message += failing_point
raise exc_type(message)
if not s3acl_only:
call_func()
with patch('swift3.cfg.CONF.s3_acl', True):
owner = Owner('test:tester', 'test:tester')
instance = args[0]
generate_s3acl_environ('test', instance.swift, owner)
call_func(' (fail at s3_acl)')
return s3acl_decorator
def _gen_test_headers(owner, grants=[], resource='container'):
if not grants:
grants = [Grant(User('test:tester'), 'FULL_CONTROL')]
@ -607,5 +651,41 @@ class TestSwift3S3Acl(Swift3TestCase):
self._test_object_copy('test:other', 'READ')
self.assertEquals(status.split()[0], '403')
def test_s3acl_decorator(self):
@s3acl
def non_class_s3acl_error():
raise
class FakeClass(object):
def __init__(self):
self.swift = MagicMock()
@s3acl
def s3acl_error(self):
raise
@s3acl
def s3acl_assert_fail(self):
assert False
@s3acl(s3acl_only=True)
def s3acl_s3only_error(self):
if CONF.s3_acl:
raise
@s3acl(s3acl_only=True)
def s3acl_s3only_no_error(self):
if not CONF.s3_acl:
raise
fake_class = FakeClass()
self.assertRaises(NotMethodException, non_class_s3acl_error)
self.assertRaises(TypeError, fake_class.s3acl_error)
self.assertRaises(AssertionError, fake_class.s3acl_assert_fail)
self.assertRaises(TypeError, fake_class.s3acl_s3only_error)
self.assertEquals(None, fake_class.s3acl_s3only_no_error())
if __name__ == '__main__':
unittest.main()