Merge from trunk
This commit is contained in:
commit
b48f5091ea
@ -120,13 +120,16 @@ def do_start(server, once=False):
|
||||
elif os.path.exists('/etc/swift/%s-server/' % server_type):
|
||||
# found config directory, searching for config file(s)
|
||||
launch_args = []
|
||||
for num, ini_file in enumerate(glob.glob('/etc/swift/%s-server/*.conf' % server_type)):
|
||||
for num, ini_file in enumerate(glob.glob('/etc/swift/%s-server/*.conf' \
|
||||
% server_type)):
|
||||
pid_file = '/var/run/swift/%s/%d.pid' % (server, num)
|
||||
# start a server for each ini_file found
|
||||
launch_args.append((ini_file, pid_file))
|
||||
else:
|
||||
# maybe there's a config file(s) out there, but I couldn't find it!
|
||||
sys.exit('Unable to locate config file for %s. %s does not exist?' % (server, ini_file))
|
||||
print 'Unable to locate config file for %s. %s does not exist?' % \
|
||||
(server, ini_file)
|
||||
return
|
||||
|
||||
# start all servers
|
||||
for ini_file, pid_file in launch_args:
|
||||
|
@ -6,6 +6,8 @@ Administrator's Guide
|
||||
Managing the Rings
|
||||
------------------
|
||||
|
||||
You need to build the storage rings on the proxy server node, and distribute them to all the servers in the cluster. Storage rings contain information about all the Swift storage partitions and how they are distributed between the different nodes and disks. For more information see :doc:`overview_ring`.
|
||||
|
||||
Removing a device from the ring::
|
||||
|
||||
swift-ring-builder <builder-file> remove <ip_address>/<device_name>
|
||||
@ -30,6 +32,30 @@ Once you are done with all changes to the ring, the changes need to be
|
||||
Once the new rings are built, they should be pushed out to all the servers
|
||||
in the cluster.
|
||||
|
||||
-----------------------
|
||||
Scripting Ring Creation
|
||||
-----------------------
|
||||
You can create scripts to create the account and container rings and rebalance. Here's an example script for the Account ring. Use similar commands to create a make-container-ring.sh script on the proxy server node.
|
||||
|
||||
1. Create a script file called make-account-ring.sh on the proxy server node with the following content::
|
||||
|
||||
#!/bin/bash
|
||||
cd /etc/swift
|
||||
rm -f account.builder account.ring.gz backups/account.builder backups/account.ring.gz
|
||||
swift-ring-builder account.builder create 18 3 1
|
||||
swift-ring-builder account.builder add z1-<account-server-1>:6002/sdb1 1
|
||||
swift-ring-builder account.builder add z2-<account-server-2>:6002/sdb1 1
|
||||
swift-ring-builder account.builder rebalance
|
||||
|
||||
You need to replace the values of <account-server-1>, <account-server-2>, etc. with the IP addresses of the account servers used in your setup. You can have as many account servers as you need. All account servers are assumed to be listening on port 6002, and have a storage device called "sdb1" (this is a directory name created under /drives when we setup the account server). The "z1", "z2", etc. designate zones, and you can choose whether you put devices in the same or different zones.
|
||||
|
||||
2. Make the script file executable and run it to create the account ring file::
|
||||
|
||||
chmod +x make-account-ring.sh
|
||||
sudo ./make-account-ring.sh
|
||||
|
||||
3. Copy the resulting ring file /etc/swift/account.ring.gz to all the account server nodes in your Swift environment, and put them in the /etc/swift directory on these nodes. Make sure that every time you change the account ring configuration, you copy the resulting ring file to all the account nodes.
|
||||
|
||||
-----------------------
|
||||
Handling System Updates
|
||||
-----------------------
|
||||
|
@ -462,6 +462,8 @@ error_suppression_interval 60 Time in seconds that must
|
||||
no longer error limited
|
||||
error_suppression_limit 10 Error count to consider a
|
||||
node error limited
|
||||
allow_account_management false Whether account PUTs and DELETEs
|
||||
are even callable
|
||||
============================ =============== =============================
|
||||
|
||||
[auth]
|
||||
|
@ -33,7 +33,7 @@ Installing dependencies and the core code
|
||||
python-xattr sqlite3 xfsprogs python-webob python-eventlet
|
||||
python-greenlet python-pastedeploy`
|
||||
#. Install anything else you want, like screen, ssh, vim, etc.
|
||||
#. Next, choose either see :ref:`partition-section` or :ref:`loopback-section`.
|
||||
#. Next, choose either :ref:`partition-section` or :ref:`loopback-section`.
|
||||
|
||||
|
||||
.. _partition-section:
|
||||
@ -241,6 +241,7 @@ Sample configuration files are provided with all defaults in line-by-line commen
|
||||
|
||||
[app:proxy-server]
|
||||
use = egg:swift#proxy
|
||||
allow_account_management = true
|
||||
|
||||
[filter:auth]
|
||||
use = egg:swift#auth
|
||||
|
@ -124,6 +124,7 @@ Configure the Proxy node
|
||||
|
||||
[app:proxy-server]
|
||||
use = egg:swift#proxy
|
||||
allow_account_management = true
|
||||
|
||||
[filter:auth]
|
||||
use = egg:swift#auth
|
||||
|
@ -29,6 +29,9 @@ use = egg:swift#proxy
|
||||
# error_suppression_interval = 60
|
||||
# How many errors can accumulate before a node is temporarily ignored.
|
||||
# error_suppression_limit = 10
|
||||
# If set to 'true' any authorized user may create and delete accounts; if
|
||||
# 'false' no one, even authorized, can.
|
||||
# allow_account_management = false
|
||||
|
||||
[filter:auth]
|
||||
use = egg:swift#auth
|
||||
|
@ -979,7 +979,7 @@ class ContainerBroker(DatabaseBroker):
|
||||
FROM object WHERE'''
|
||||
query_args = []
|
||||
if end_marker:
|
||||
query += ' name <= ? AND'
|
||||
query += ' name < ? AND'
|
||||
query_args.append(end_marker)
|
||||
if marker and marker >= prefix:
|
||||
query += ' name > ? AND'
|
||||
|
@ -93,10 +93,11 @@ def delay_denial(func):
|
||||
return func(*a, **kw)
|
||||
return wrapped
|
||||
|
||||
def get_account_memcache_key(account):
|
||||
return 'account/%s' % account
|
||||
|
||||
def get_container_memcache_key(account, container):
|
||||
path = '/%s/%s' % (account, container)
|
||||
return 'container%s' % path
|
||||
return 'container/%s/%s' % (account, container)
|
||||
|
||||
|
||||
class SegmentedIterable(object):
|
||||
@ -329,13 +330,17 @@ class Controller(object):
|
||||
if it does not exist
|
||||
"""
|
||||
partition, nodes = self.app.account_ring.get_nodes(account)
|
||||
path = '/%s' % account
|
||||
cache_key = 'account%s' % path
|
||||
# 0 = no responses, 200 = found, 404 = not found, -1 = mixed responses
|
||||
if self.app.memcache and self.app.memcache.get(cache_key) == 200:
|
||||
return partition, nodes
|
||||
if self.app.memcache:
|
||||
cache_key = get_account_memcache_key(account)
|
||||
result_code = self.app.memcache.get(cache_key)
|
||||
if result_code == 200:
|
||||
return partition, nodes
|
||||
elif result_code == 404:
|
||||
return None, None
|
||||
result_code = 0
|
||||
attempts_left = self.app.account_ring.replica_count
|
||||
path = '/%s' % account
|
||||
headers = {'x-cf-trans-id': self.trans_id}
|
||||
for node in self.iter_nodes(partition, nodes, self.app.account_ring):
|
||||
if self.error_limited(node):
|
||||
@ -366,16 +371,16 @@ class Controller(object):
|
||||
except:
|
||||
self.exception_occurred(node, 'Account',
|
||||
'Trying to get account info for %s' % path)
|
||||
if result_code == 200:
|
||||
cache_timeout = self.app.recheck_account_existence
|
||||
else:
|
||||
cache_timeout = self.app.recheck_account_existence * 0.1
|
||||
if self.app.memcache:
|
||||
if self.app.memcache and result_code in (200, 404):
|
||||
if result_code == 200:
|
||||
cache_timeout = self.app.recheck_account_existence
|
||||
else:
|
||||
cache_timeout = self.app.recheck_account_existence * 0.1
|
||||
self.app.memcache.set(cache_key, result_code,
|
||||
timeout=cache_timeout)
|
||||
if result_code == 200:
|
||||
return partition, nodes
|
||||
return (None, None)
|
||||
return None, None
|
||||
|
||||
def container_info(self, account, container):
|
||||
"""
|
||||
@ -392,7 +397,6 @@ class Controller(object):
|
||||
partition, nodes = self.app.container_ring.get_nodes(
|
||||
account, container)
|
||||
path = '/%s/%s' % (account, container)
|
||||
cache_key = None
|
||||
if self.app.memcache:
|
||||
cache_key = get_container_memcache_key(account, container)
|
||||
cache_value = self.app.memcache.get(cache_key)
|
||||
@ -402,8 +406,10 @@ class Controller(object):
|
||||
write_acl = cache_value['write_acl']
|
||||
if status == 200:
|
||||
return partition, nodes, read_acl, write_acl
|
||||
elif status == 404:
|
||||
return None, None, None, None
|
||||
if not self.account_info(account)[1]:
|
||||
return (None, None, None, None)
|
||||
return None, None, None, None
|
||||
result_code = 0
|
||||
read_acl = None
|
||||
write_acl = None
|
||||
@ -443,11 +449,11 @@ class Controller(object):
|
||||
except:
|
||||
self.exception_occurred(node, 'Container',
|
||||
'Trying to get container info for %s' % path)
|
||||
if result_code == 200:
|
||||
cache_timeout = self.app.recheck_container_existence
|
||||
else:
|
||||
cache_timeout = self.app.recheck_container_existence * 0.1
|
||||
if cache_key and self.app.memcache:
|
||||
if self.app.memcache and result_code in (200, 404):
|
||||
if result_code == 200:
|
||||
cache_timeout = self.app.recheck_container_existence
|
||||
else:
|
||||
cache_timeout = self.app.recheck_container_existence * 0.1
|
||||
self.app.memcache.set(cache_key,
|
||||
{'status': result_code,
|
||||
'read_acl': read_acl,
|
||||
@ -456,7 +462,7 @@ class Controller(object):
|
||||
timeout=cache_timeout)
|
||||
if result_code == 200:
|
||||
return partition, nodes, read_acl, write_acl
|
||||
return (None, None, None, None)
|
||||
return None, None, None, None
|
||||
|
||||
def iter_nodes(self, partition, nodes, ring):
|
||||
"""
|
||||
@ -1338,6 +1344,8 @@ class AccountController(Controller):
|
||||
@public
|
||||
def PUT(self, req):
|
||||
"""HTTP PUT request handler."""
|
||||
if not self.app.allow_account_management:
|
||||
return HTTPMethodNotAllowed(request=req)
|
||||
error_response = check_metadata(req, 'account')
|
||||
if error_response:
|
||||
return error_response
|
||||
@ -1437,6 +1445,51 @@ class AccountController(Controller):
|
||||
return self.best_response(req, statuses, reasons, bodies,
|
||||
'Account POST')
|
||||
|
||||
@public
|
||||
def DELETE(self, req):
|
||||
"""HTTP DELETE request handler."""
|
||||
if not self.app.allow_account_management:
|
||||
return HTTPMethodNotAllowed(request=req)
|
||||
account_partition, accounts = \
|
||||
self.app.account_ring.get_nodes(self.account_name)
|
||||
headers = {'X-Timestamp': normalize_timestamp(time.time()),
|
||||
'X-CF-Trans-Id': self.trans_id}
|
||||
statuses = []
|
||||
reasons = []
|
||||
bodies = []
|
||||
for node in self.iter_nodes(account_partition, accounts,
|
||||
self.app.account_ring):
|
||||
if self.error_limited(node):
|
||||
continue
|
||||
try:
|
||||
with ConnectionTimeout(self.app.conn_timeout):
|
||||
conn = http_connect(node['ip'], node['port'],
|
||||
node['device'], account_partition, 'DELETE',
|
||||
req.path_info, headers)
|
||||
with Timeout(self.app.node_timeout):
|
||||
source = conn.getresponse()
|
||||
body = source.read()
|
||||
if 200 <= source.status < 300 \
|
||||
or 400 <= source.status < 500:
|
||||
statuses.append(source.status)
|
||||
reasons.append(source.reason)
|
||||
bodies.append(body)
|
||||
elif source.status == 507:
|
||||
self.error_limit(node)
|
||||
except:
|
||||
self.exception_occurred(node, 'Account',
|
||||
'Trying to DELETE %s' % req.path)
|
||||
if len(statuses) >= len(accounts):
|
||||
break
|
||||
while len(statuses) < len(accounts):
|
||||
statuses.append(503)
|
||||
reasons.append('')
|
||||
bodies.append('')
|
||||
if self.app.memcache:
|
||||
self.app.memcache.delete('account%s' % req.path_info.rstrip('/'))
|
||||
return self.best_response(req, statuses, reasons, bodies,
|
||||
'Account DELETE')
|
||||
|
||||
|
||||
class BaseApplication(object):
|
||||
"""Base WSGI application for the proxy server"""
|
||||
@ -1464,6 +1517,8 @@ class BaseApplication(object):
|
||||
int(conf.get('recheck_container_existence', 60))
|
||||
self.recheck_account_existence = \
|
||||
int(conf.get('recheck_account_existence', 60))
|
||||
self.allow_account_management = \
|
||||
conf.get('allow_account_management', 'false').lower() == 'true'
|
||||
self.resellers_conf = ConfigParser()
|
||||
self.resellers_conf.read(os.path.join(swift_dir, 'resellers.conf'))
|
||||
self.object_ring = object_ring or \
|
||||
|
@ -137,11 +137,11 @@ class LogProcessor(object):
|
||||
year = '%04d' % parsed_date.tm_year
|
||||
month = '%02d' % parsed_date.tm_mon
|
||||
day = '%02d' % parsed_date.tm_mday
|
||||
hour = '%02d' % parsed_date.tm_hour
|
||||
# Since the end_marker filters by <=, we need to add something
|
||||
# to then end_key to make sure we get all the data under the
|
||||
# last hour. Adding '/\x7f' should be all inclusive.
|
||||
end_key = '/'.join([year, month, day, hour]) + '/\x7f'
|
||||
# Since the end_marker filters by <, we need to add something
|
||||
# to make sure we get all the data under the last hour. Adding
|
||||
# one to the hour should be all-inclusive.
|
||||
hour = '%02d' % (parsed_date.tm_hour + 1)
|
||||
end_key = '/'.join([year, month, day, hour])
|
||||
container_listing = self.internal_proxy.get_container_list(
|
||||
swift_account,
|
||||
container_name,
|
||||
|
@ -314,12 +314,19 @@ class TestAuth(unittest.TestCase):
|
||||
|
||||
def test_authorize_bad_path(self):
|
||||
req = Request.blank('/badpath')
|
||||
resp = str(self.test_auth.authorize(req))
|
||||
self.assert_(resp.startswith('401'), resp)
|
||||
resp = self.test_auth.authorize(req)
|
||||
self.assertEquals(resp and resp.status_int, 401)
|
||||
req = Request.blank('/badpath')
|
||||
req.remote_user = 'act:usr,act,AUTH_cfa'
|
||||
resp = str(self.test_auth.authorize(req))
|
||||
self.assert_(resp.startswith('403'), resp)
|
||||
resp = self.test_auth.authorize(req)
|
||||
self.assertEquals(resp and resp.status_int, 403)
|
||||
req = Request.blank('')
|
||||
resp = self.test_auth.authorize(req)
|
||||
self.assertEquals(resp and resp.status_int, 404)
|
||||
req = Request.blank('')
|
||||
req.environ['swift.cache'] = FakeMemcache()
|
||||
result = ''.join(self.test_auth(req.environ, lambda x, y: None))
|
||||
self.assert_(result.startswith('404'), result)
|
||||
|
||||
def test_authorize_account_access(self):
|
||||
req = Request.blank('/v1/AUTH_cfa')
|
||||
@ -327,14 +334,14 @@ class TestAuth(unittest.TestCase):
|
||||
self.assertEquals(self.test_auth.authorize(req), None)
|
||||
req = Request.blank('/v1/AUTH_cfa')
|
||||
req.remote_user = 'act:usr,act'
|
||||
resp = str(self.test_auth.authorize(req))
|
||||
self.assert_(resp.startswith('403'), resp)
|
||||
resp = self.test_auth.authorize(req)
|
||||
self.assertEquals(resp and resp.status_int, 403)
|
||||
|
||||
def test_authorize_acl_group_access(self):
|
||||
req = Request.blank('/v1/AUTH_cfa')
|
||||
req.remote_user = 'act:usr,act'
|
||||
resp = str(self.test_auth.authorize(req))
|
||||
self.assert_(resp.startswith('403'), resp)
|
||||
resp = self.test_auth.authorize(req)
|
||||
self.assertEquals(resp and resp.status_int, 403)
|
||||
req = Request.blank('/v1/AUTH_cfa')
|
||||
req.remote_user = 'act:usr,act'
|
||||
req.acl = 'act'
|
||||
@ -346,27 +353,27 @@ class TestAuth(unittest.TestCase):
|
||||
req = Request.blank('/v1/AUTH_cfa')
|
||||
req.remote_user = 'act:usr,act'
|
||||
req.acl = 'act2'
|
||||
resp = str(self.test_auth.authorize(req))
|
||||
self.assert_(resp.startswith('403'), resp)
|
||||
resp = self.test_auth.authorize(req)
|
||||
self.assertEquals(resp and resp.status_int, 403)
|
||||
req = Request.blank('/v1/AUTH_cfa')
|
||||
req.remote_user = 'act:usr,act'
|
||||
req.acl = 'act:usr2'
|
||||
resp = str(self.test_auth.authorize(req))
|
||||
self.assert_(resp.startswith('403'), resp)
|
||||
resp = self.test_auth.authorize(req)
|
||||
self.assertEquals(resp and resp.status_int, 403)
|
||||
|
||||
def test_deny_cross_reseller(self):
|
||||
# Tests that cross-reseller is denied, even if ACLs/group names match
|
||||
req = Request.blank('/v1/OTHER_cfa')
|
||||
req.remote_user = 'act:usr,act,AUTH_cfa'
|
||||
req.acl = 'act'
|
||||
resp = str(self.test_auth.authorize(req))
|
||||
self.assert_(resp.startswith('403'), resp)
|
||||
resp = self.test_auth.authorize(req)
|
||||
self.assertEquals(resp and resp.status_int, 403)
|
||||
|
||||
def test_authorize_acl_referrer_access(self):
|
||||
req = Request.blank('/v1/AUTH_cfa')
|
||||
req.remote_user = 'act:usr,act'
|
||||
resp = str(self.test_auth.authorize(req))
|
||||
self.assert_(resp.startswith('403'), resp)
|
||||
resp = self.test_auth.authorize(req)
|
||||
self.assertEquals(resp and resp.status_int, 403)
|
||||
req = Request.blank('/v1/AUTH_cfa')
|
||||
req.remote_user = 'act:usr,act'
|
||||
req.acl = '.r:*'
|
||||
@ -374,23 +381,23 @@ class TestAuth(unittest.TestCase):
|
||||
req = Request.blank('/v1/AUTH_cfa')
|
||||
req.remote_user = 'act:usr,act'
|
||||
req.acl = '.r:.example.com'
|
||||
resp = str(self.test_auth.authorize(req))
|
||||
self.assert_(resp.startswith('403'), resp)
|
||||
resp = self.test_auth.authorize(req)
|
||||
self.assertEquals(resp and resp.status_int, 403)
|
||||
req = Request.blank('/v1/AUTH_cfa')
|
||||
req.remote_user = 'act:usr,act'
|
||||
req.referer = 'http://www.example.com/index.html'
|
||||
req.acl = '.r:.example.com'
|
||||
self.assertEquals(self.test_auth.authorize(req), None)
|
||||
req = Request.blank('/v1/AUTH_cfa')
|
||||
resp = str(self.test_auth.authorize(req))
|
||||
self.assert_(resp.startswith('401'), resp)
|
||||
resp = self.test_auth.authorize(req)
|
||||
self.assertEquals(resp and resp.status_int, 401)
|
||||
req = Request.blank('/v1/AUTH_cfa')
|
||||
req.acl = '.r:*'
|
||||
self.assertEquals(self.test_auth.authorize(req), None)
|
||||
req = Request.blank('/v1/AUTH_cfa')
|
||||
req.acl = '.r:.example.com'
|
||||
resp = str(self.test_auth.authorize(req))
|
||||
self.assert_(resp.startswith('401'), resp)
|
||||
resp = self.test_auth.authorize(req)
|
||||
self.assertEquals(resp and resp.status_int, 401)
|
||||
req = Request.blank('/v1/AUTH_cfa')
|
||||
req.referer = 'http://www.example.com/index.html'
|
||||
req.acl = '.r:.example.com'
|
||||
@ -399,19 +406,19 @@ class TestAuth(unittest.TestCase):
|
||||
def test_account_put_permissions(self):
|
||||
req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'})
|
||||
req.remote_user = 'act:usr,act'
|
||||
resp = str(self.test_auth.authorize(req))
|
||||
self.assert_(resp.startswith('403'), resp)
|
||||
resp = self.test_auth.authorize(req)
|
||||
self.assertEquals(resp and resp.status_int, 403)
|
||||
|
||||
req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'})
|
||||
req.remote_user = 'act:usr,act,AUTH_other'
|
||||
resp = str(self.test_auth.authorize(req))
|
||||
self.assert_(resp.startswith('403'), resp)
|
||||
resp = self.test_auth.authorize(req)
|
||||
self.assertEquals(resp and resp.status_int, 403)
|
||||
|
||||
# Even PUTs to your own account as account admin should fail
|
||||
req = Request.blank('/v1/AUTH_old', environ={'REQUEST_METHOD': 'PUT'})
|
||||
req.remote_user = 'act:usr,act,AUTH_old'
|
||||
resp = str(self.test_auth.authorize(req))
|
||||
self.assert_(resp.startswith('403'), resp)
|
||||
resp = self.test_auth.authorize(req)
|
||||
self.assertEquals(resp and resp.status_int, 403)
|
||||
|
||||
req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'})
|
||||
req.remote_user = 'act:usr,act,.reseller_admin'
|
||||
@ -423,8 +430,7 @@ class TestAuth(unittest.TestCase):
|
||||
req = Request.blank('/v1/AUTH_new', environ={'REQUEST_METHOD': 'PUT'})
|
||||
req.remote_user = 'act:usr,act,.super_admin'
|
||||
resp = self.test_auth.authorize(req)
|
||||
resp = str(self.test_auth.authorize(req))
|
||||
self.assert_(resp.startswith('403'), resp)
|
||||
self.assertEquals(resp and resp.status_int, 403)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -984,9 +984,9 @@ class TestContainerBroker(unittest.TestCase):
|
||||
self.assertEquals(listing[-1][0], '0/0099')
|
||||
|
||||
listing = broker.list_objects_iter(100, '', '0/0050', None, '')
|
||||
self.assertEquals(len(listing), 51)
|
||||
self.assertEquals(len(listing), 50)
|
||||
self.assertEquals(listing[0][0], '0/0000')
|
||||
self.assertEquals(listing[-1][0], '0/0050')
|
||||
self.assertEquals(listing[-1][0], '0/0049')
|
||||
|
||||
listing = broker.list_objects_iter(100, '0/0099', None, None, '')
|
||||
self.assertEquals(len(listing), 100)
|
||||
|
@ -90,6 +90,8 @@ def fake_http_connect(*code_iter, **kwargs):
|
||||
pass
|
||||
if 'slow' in kwargs:
|
||||
headers['content-length'] = '4'
|
||||
if 'headers' in kwargs:
|
||||
headers.update(kwargs['headers'])
|
||||
return headers.items()
|
||||
|
||||
def read(self, amt=None):
|
||||
@ -167,6 +169,9 @@ class FakeMemcache(object):
|
||||
def get(self, key):
|
||||
return self.store.get(key)
|
||||
|
||||
def keys(self):
|
||||
return self.store.keys()
|
||||
|
||||
def set(self, key, value, timeout=0):
|
||||
self.store[key] = value
|
||||
return True
|
||||
@ -204,10 +209,12 @@ class NullLoggingHandler(logging.Handler):
|
||||
@contextmanager
|
||||
def save_globals():
|
||||
orig_http_connect = getattr(proxy_server, 'http_connect', None)
|
||||
orig_account_info = getattr(proxy_server.Controller, 'account_info', None)
|
||||
try:
|
||||
yield True
|
||||
finally:
|
||||
proxy_server.http_connect = orig_http_connect
|
||||
proxy_server.Controller.account_info = orig_account_info
|
||||
|
||||
# tests
|
||||
|
||||
@ -215,63 +222,155 @@ class TestController(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.account_ring = FakeRing()
|
||||
self.container_ring = FakeRing()
|
||||
self.memcache = FakeMemcache()
|
||||
|
||||
app = proxy_server.Application(None, FakeMemcache(),
|
||||
account_ring=self.account_ring, container_ring=FakeRing(),
|
||||
app = proxy_server.Application(None, self.memcache,
|
||||
account_ring=self.account_ring,
|
||||
container_ring=self.container_ring,
|
||||
object_ring=FakeRing())
|
||||
self.controller = proxy_server.Controller(app)
|
||||
|
||||
def check_account_info_return(self, account, partition, nodes):
|
||||
p, n = self.account_ring.get_nodes(account)
|
||||
self.account = 'some_account'
|
||||
self.container = 'some_container'
|
||||
self.read_acl = 'read_acl'
|
||||
self.write_acl = 'write_acl'
|
||||
|
||||
def check_account_info_return(self, partition, nodes, is_none=False):
|
||||
if is_none:
|
||||
p, n = None, None
|
||||
else:
|
||||
p, n = self.account_ring.get_nodes(self.account)
|
||||
self.assertEqual(p, partition)
|
||||
self.assertEqual(n, nodes)
|
||||
|
||||
def test_account_info_404_200(self):
|
||||
account = 'test_account_info_404_200'
|
||||
|
||||
with save_globals():
|
||||
proxy_server.http_connect = fake_http_connect(404, 404, 404)
|
||||
partition, nodes = self.controller.account_info(account)
|
||||
self.assertEqual(partition, None)
|
||||
self.assertEqual(nodes, None)
|
||||
|
||||
proxy_server.http_connect = fake_http_connect(200)
|
||||
partition, nodes = self.controller.account_info(account)
|
||||
self.check_account_info_return(account, partition, nodes)
|
||||
|
||||
def test_account_info_404(self):
|
||||
account = 'test_account_info_404'
|
||||
|
||||
with save_globals():
|
||||
proxy_server.http_connect = fake_http_connect(404, 404, 404)
|
||||
partition, nodes = self.controller.account_info(account)
|
||||
self.assertEqual(partition, None)
|
||||
self.assertEqual(nodes, None)
|
||||
|
||||
proxy_server.http_connect = fake_http_connect(404, 404, 404)
|
||||
partition, nodes = self.controller.account_info(account)
|
||||
self.assertEqual(partition, None)
|
||||
self.assertEqual(nodes, None)
|
||||
|
||||
# tests if 200 is cached and used
|
||||
def test_account_info_200(self):
|
||||
account = 'test_account_info_200'
|
||||
|
||||
with save_globals():
|
||||
proxy_server.http_connect = fake_http_connect(200)
|
||||
partition, nodes = self.controller.account_info(account)
|
||||
self.check_account_info_return(account, partition, nodes)
|
||||
partition, nodes = self.controller.account_info(self.account)
|
||||
self.check_account_info_return(partition, nodes)
|
||||
|
||||
def test_account_info_200_200(self):
|
||||
account = 'test_account_info_200_200'
|
||||
cache_key = proxy_server.get_account_memcache_key(self.account)
|
||||
self.assertEquals(200, self.memcache.get(cache_key))
|
||||
|
||||
proxy_server.http_connect = fake_http_connect()
|
||||
partition, nodes = self.controller.account_info(self.account)
|
||||
self.check_account_info_return(partition, nodes)
|
||||
|
||||
# tests if 404 is cached and used
|
||||
def test_account_info_404(self):
|
||||
with save_globals():
|
||||
proxy_server.http_connect = fake_http_connect(404, 404, 404)
|
||||
partition, nodes = self.controller.account_info(self.account)
|
||||
self.check_account_info_return(partition, nodes, True)
|
||||
|
||||
cache_key = proxy_server.get_account_memcache_key(self.account)
|
||||
self.assertEquals(404, self.memcache.get(cache_key))
|
||||
|
||||
proxy_server.http_connect = fake_http_connect()
|
||||
partition, nodes = self.controller.account_info(self.account)
|
||||
self.check_account_info_return(partition, nodes, True)
|
||||
|
||||
# tests if some http status codes are not cached
|
||||
def test_account_info_no_cache(self):
|
||||
def test(*status_list):
|
||||
proxy_server.http_connect = fake_http_connect(*status_list)
|
||||
partition, nodes = self.controller.account_info(self.account)
|
||||
self.assertEqual(len(self.memcache.keys()), 0)
|
||||
self.check_account_info_return(partition, nodes, True)
|
||||
|
||||
with save_globals():
|
||||
proxy_server.http_connect = fake_http_connect(200)
|
||||
partition, nodes = self.controller.account_info(account)
|
||||
self.check_account_info_return(account, partition, nodes)
|
||||
test(503, 404, 404)
|
||||
test(404, 404, 503)
|
||||
test(404, 507, 503)
|
||||
test(503, 503, 503)
|
||||
|
||||
proxy_server.http_connect = fake_http_connect(200)
|
||||
partition, nodes = self.controller.account_info(account)
|
||||
self.check_account_info_return(account, partition, nodes)
|
||||
def check_container_info_return(self, ret, is_none=False):
|
||||
if is_none:
|
||||
partition, nodes, read_acl, write_acl = None, None, None, None
|
||||
else:
|
||||
partition, nodes = self.container_ring.get_nodes(self.account,
|
||||
self.container)
|
||||
read_acl, write_acl = self.read_acl, self.write_acl
|
||||
self.assertEqual(partition, ret[0])
|
||||
self.assertEqual(nodes, ret[1])
|
||||
self.assertEqual(read_acl, ret[2])
|
||||
self.assertEqual(write_acl, ret[3])
|
||||
|
||||
def test_container_info_invalid_account(self):
|
||||
def account_info(self, account):
|
||||
return None, None
|
||||
|
||||
with save_globals():
|
||||
proxy_server.Controller.account_info = account_info
|
||||
ret = self.controller.container_info(self.account,
|
||||
self.container)
|
||||
self.check_container_info_return(ret, True)
|
||||
|
||||
# tests if 200 is cached and used
|
||||
def test_container_info_200(self):
|
||||
def account_info(self, account):
|
||||
return True, True
|
||||
|
||||
with save_globals():
|
||||
headers = {'x-container-read': self.read_acl,
|
||||
'x-container-write': self.write_acl}
|
||||
proxy_server.Controller.account_info = account_info
|
||||
proxy_server.http_connect = fake_http_connect(200,
|
||||
headers=headers)
|
||||
ret = self.controller.container_info(self.account,
|
||||
self.container)
|
||||
self.check_container_info_return(ret)
|
||||
|
||||
cache_key = proxy_server.get_container_memcache_key(self.account,
|
||||
self.container)
|
||||
cache_value = self.memcache.get(cache_key)
|
||||
self.assertEquals(dict, type(cache_value))
|
||||
self.assertEquals(200, cache_value.get('status'))
|
||||
|
||||
proxy_server.http_connect = fake_http_connect()
|
||||
ret = self.controller.container_info(self.account,
|
||||
self.container)
|
||||
self.check_container_info_return(ret)
|
||||
|
||||
# tests if 404 is cached and used
|
||||
def test_container_info_404(self):
|
||||
def account_info(self, account):
|
||||
return True, True
|
||||
|
||||
with save_globals():
|
||||
proxy_server.Controller.account_info = account_info
|
||||
proxy_server.http_connect = fake_http_connect(404, 404, 404)
|
||||
ret = self.controller.container_info(self.account,
|
||||
self.container)
|
||||
self.check_container_info_return(ret, True)
|
||||
|
||||
cache_key = proxy_server.get_container_memcache_key(self.account,
|
||||
self.container)
|
||||
cache_value = self.memcache.get(cache_key)
|
||||
self.assertEquals(dict, type(cache_value))
|
||||
self.assertEquals(404, cache_value.get('status'))
|
||||
|
||||
proxy_server.http_connect = fake_http_connect()
|
||||
ret = self.controller.container_info(self.account,
|
||||
self.container)
|
||||
self.check_container_info_return(ret, True)
|
||||
|
||||
# tests if some http status codes are not cached
|
||||
def test_container_info_no_cache(self):
|
||||
def test(*status_list):
|
||||
proxy_server.http_connect = fake_http_connect(*status_list)
|
||||
ret = self.controller.container_info(self.account,
|
||||
self.container)
|
||||
self.assertEqual(len(self.memcache.keys()), 0)
|
||||
self.check_container_info_return(ret, True)
|
||||
|
||||
with save_globals():
|
||||
test(503, 404, 404)
|
||||
test(404, 404, 503)
|
||||
test(404, 507, 503)
|
||||
test(503, 503, 503)
|
||||
|
||||
class TestProxyServer(unittest.TestCase):
|
||||
|
||||
@ -2681,6 +2780,8 @@ class TestAccountController(unittest.TestCase):
|
||||
res = controller.PUT(req)
|
||||
expected = str(expected)
|
||||
self.assertEquals(res.status[:len(expected)], expected)
|
||||
test_status_map((201, 201, 201), 405)
|
||||
self.app.allow_account_management = True
|
||||
test_status_map((201, 201, 201), 201)
|
||||
test_status_map((201, 201, 500), 201)
|
||||
test_status_map((201, 500, 500), 503)
|
||||
@ -2688,6 +2789,7 @@ class TestAccountController(unittest.TestCase):
|
||||
|
||||
def test_PUT_max_account_name_length(self):
|
||||
with save_globals():
|
||||
self.app.allow_account_management = True
|
||||
controller = proxy_server.AccountController(self.app, '1' * 256)
|
||||
self.assert_status_map(controller.PUT, (201, 201, 201), 201)
|
||||
controller = proxy_server.AccountController(self.app, '2' * 257)
|
||||
@ -2695,6 +2797,7 @@ class TestAccountController(unittest.TestCase):
|
||||
|
||||
def test_PUT_connect_exceptions(self):
|
||||
with save_globals():
|
||||
self.app.allow_account_management = True
|
||||
controller = proxy_server.AccountController(self.app, 'account')
|
||||
self.assert_status_map(controller.PUT, (201, 201, -1), 201)
|
||||
self.assert_status_map(controller.PUT, (201, -1, -1), 503)
|
||||
@ -2723,6 +2826,7 @@ class TestAccountController(unittest.TestCase):
|
||||
test_errors.append('%s: %s not in %s' %
|
||||
(test_header, test_value, headers))
|
||||
with save_globals():
|
||||
self.app.allow_account_management = True
|
||||
controller = \
|
||||
proxy_server.AccountController(self.app, 'a')
|
||||
proxy_server.http_connect = fake_http_connect(201, 201, 201,
|
||||
@ -2741,6 +2845,7 @@ class TestAccountController(unittest.TestCase):
|
||||
|
||||
def bad_metadata_helper(self, method):
|
||||
with save_globals():
|
||||
self.app.allow_account_management = True
|
||||
controller = proxy_server.AccountController(self.app, 'a')
|
||||
proxy_server.http_connect = fake_http_connect(200, 201, 201, 201)
|
||||
req = Request.blank('/a/c', environ={'REQUEST_METHOD': method})
|
||||
@ -2823,6 +2928,27 @@ class TestAccountController(unittest.TestCase):
|
||||
resp = getattr(controller, method)(req)
|
||||
self.assertEquals(resp.status_int, 400)
|
||||
|
||||
def test_DELETE(self):
|
||||
with save_globals():
|
||||
controller = proxy_server.AccountController(self.app, 'account')
|
||||
|
||||
def test_status_map(statuses, expected, **kwargs):
|
||||
proxy_server.http_connect = \
|
||||
fake_http_connect(*statuses, **kwargs)
|
||||
self.app.memcache.store = {}
|
||||
req = Request.blank('/a', {'REQUEST_METHOD': 'DELETE'})
|
||||
req.content_length = 0
|
||||
self.app.update_request(req)
|
||||
res = controller.DELETE(req)
|
||||
expected = str(expected)
|
||||
self.assertEquals(res.status[:len(expected)], expected)
|
||||
test_status_map((201, 201, 201), 405)
|
||||
self.app.allow_account_management = True
|
||||
test_status_map((201, 201, 201), 201)
|
||||
test_status_map((201, 201, 500), 201)
|
||||
test_status_map((201, 500, 500), 503)
|
||||
test_status_map((204, 500, 404), 503)
|
||||
|
||||
|
||||
class FakeObjectController(object):
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user