Implemented object POST as COPY
This commit is contained in:
parent
37fbf5ab77
commit
7c9e542c02
@ -547,6 +547,16 @@ error_suppression_limit 10 Error count to consider a
|
||||
node error limited
|
||||
allow_account_management false Whether account PUTs and DELETEs
|
||||
are even callable
|
||||
post_as_copy true Set post_as_copy = false to turn
|
||||
on fast posts where only the
|
||||
metadata changes are stored anew
|
||||
and the original data file is
|
||||
kept in place. This makes for
|
||||
quicker posts; but since the
|
||||
container metadata isn't updated
|
||||
in this mode, features like
|
||||
container sync won't be able to
|
||||
sync posts.
|
||||
============================ =============== =============================
|
||||
|
||||
[auth]
|
||||
|
@ -40,6 +40,11 @@ use = egg:swift#proxy
|
||||
# If set to 'true' any authorized user may create and delete accounts; if
|
||||
# 'false' no one, even authorized, can.
|
||||
# allow_account_management = false
|
||||
# Set post_as_copy = false to turn on fast posts where only the metadata
|
||||
# changes are stored anew and the original data file is kept in place. This
|
||||
# makes for quicker posts; but since the container metadata isn't updated in
|
||||
# this mode, features like container sync won't be able to sync posts.
|
||||
# post_as_copy = true
|
||||
|
||||
[filter:swauth]
|
||||
use = egg:swift#swauth
|
||||
|
@ -36,6 +36,57 @@ def quote(value, safe='/'):
|
||||
return _quote(value, safe)
|
||||
|
||||
|
||||
def direct_get_account(node, part, account, marker=None, limit=None,
|
||||
prefix=None, delimiter=None, conn_timeout=5,
|
||||
response_timeout=15):
|
||||
"""
|
||||
Get listings directly from the account server.
|
||||
|
||||
:param node: node dictionary from the ring
|
||||
:param part: partition the account is on
|
||||
:param account: account name
|
||||
:param marker: marker query
|
||||
:param limit: query limit
|
||||
:param prefix: prefix query
|
||||
:param delimeter: delimeter for the query
|
||||
:param conn_timeout: timeout in seconds for establishing the connection
|
||||
:param response_timeout: timeout in seconds for getting the response
|
||||
:returns: a tuple of (response headers, a list of containers) The response
|
||||
headers will be a dict and all header names will be lowercase.
|
||||
"""
|
||||
path = '/' + account
|
||||
qs = 'format=json'
|
||||
if marker:
|
||||
qs += '&marker=%s' % quote(marker)
|
||||
if limit:
|
||||
qs += '&limit=%d' % limit
|
||||
if prefix:
|
||||
qs += '&prefix=%s' % quote(prefix)
|
||||
if delimiter:
|
||||
qs += '&delimiter=%s' % quote(delimiter)
|
||||
with Timeout(conn_timeout):
|
||||
conn = http_connect(node['ip'], node['port'], node['device'], part,
|
||||
'GET', path, query_string='format=json')
|
||||
with Timeout(response_timeout):
|
||||
resp = conn.getresponse()
|
||||
if resp.status < 200 or resp.status >= 300:
|
||||
resp.read()
|
||||
raise ClientException(
|
||||
'Account server %s:%s direct GET %s gave status %s' % (node['ip'],
|
||||
node['port'], repr('/%s/%s%s' % (node['device'], part, path)),
|
||||
resp.status),
|
||||
http_host=node['ip'], http_port=node['port'],
|
||||
http_device=node['device'], http_status=resp.status,
|
||||
http_reason=resp.reason)
|
||||
resp_headers = {}
|
||||
for header, value in resp.getheaders():
|
||||
resp_headers[header.lower()] = value
|
||||
if resp.status == 204:
|
||||
resp.read()
|
||||
return resp_headers, []
|
||||
return resp_headers, json_loads(resp.read())
|
||||
|
||||
|
||||
def direct_head_container(node, part, account, container, conn_timeout=5,
|
||||
response_timeout=15):
|
||||
"""
|
||||
|
@ -872,29 +872,39 @@ class ObjectController(Controller):
|
||||
@delay_denial
|
||||
def POST(self, req):
|
||||
"""HTTP POST request handler."""
|
||||
error_response = check_metadata(req, 'object')
|
||||
if error_response:
|
||||
return error_response
|
||||
container_partition, containers, _junk, req.acl = \
|
||||
self.container_info(self.account_name, self.container_name)
|
||||
if 'swift.authorize' in req.environ:
|
||||
aresp = req.environ['swift.authorize'](req)
|
||||
if aresp:
|
||||
return aresp
|
||||
if not containers:
|
||||
return HTTPNotFound(request=req)
|
||||
partition, nodes = self.app.object_ring.get_nodes(
|
||||
self.account_name, self.container_name, self.object_name)
|
||||
req.headers['X-Timestamp'] = normalize_timestamp(time.time())
|
||||
headers = []
|
||||
for container in containers:
|
||||
nheaders = dict(req.headers.iteritems())
|
||||
nheaders['X-Container-Host'] = '%(ip)s:%(port)s' % container
|
||||
nheaders['X-Container-Partition'] = container_partition
|
||||
nheaders['X-Container-Device'] = container['device']
|
||||
headers.append(nheaders)
|
||||
return self.make_requests(req, self.app.object_ring,
|
||||
partition, 'POST', req.path_info, headers)
|
||||
if self.app.post_as_copy:
|
||||
req.method = 'PUT'
|
||||
req.path_info = '/%s/%s/%s' % (self.account_name,
|
||||
self.container_name, self.object_name)
|
||||
req.headers['Content-Length'] = 0
|
||||
req.headers['X-Copy-From'] = '/%s/%s' % (self.container_name,
|
||||
self.object_name)
|
||||
req.headers['X-Fresh-Metadata'] = 'true'
|
||||
return self.PUT(req)
|
||||
else:
|
||||
error_response = check_metadata(req, 'object')
|
||||
if error_response:
|
||||
return error_response
|
||||
container_partition, containers, _junk, req.acl = \
|
||||
self.container_info(self.account_name, self.container_name)
|
||||
if 'swift.authorize' in req.environ:
|
||||
aresp = req.environ['swift.authorize'](req)
|
||||
if aresp:
|
||||
return aresp
|
||||
if not containers:
|
||||
return HTTPNotFound(request=req)
|
||||
partition, nodes = self.app.object_ring.get_nodes(
|
||||
self.account_name, self.container_name, self.object_name)
|
||||
req.headers['X-Timestamp'] = normalize_timestamp(time.time())
|
||||
headers = []
|
||||
for container in containers:
|
||||
nheaders = dict(req.headers.iteritems())
|
||||
nheaders['X-Container-Host'] = '%(ip)s:%(port)s' % container
|
||||
nheaders['X-Container-Partition'] = container_partition
|
||||
nheaders['X-Container-Device'] = container['device']
|
||||
headers.append(nheaders)
|
||||
return self.make_requests(req, self.app.object_ring,
|
||||
partition, 'POST', req.path_info, headers)
|
||||
|
||||
def _send_file(self, conn, path):
|
||||
"""Method for a file PUT coro"""
|
||||
@ -998,12 +1008,14 @@ class ObjectController(Controller):
|
||||
if not content_type_manually_set:
|
||||
new_req.headers['Content-Type'] = \
|
||||
source_resp.headers['Content-Type']
|
||||
for k, v in source_resp.headers.items():
|
||||
if k.lower().startswith('x-object-meta-'):
|
||||
new_req.headers[k] = v
|
||||
for k, v in req.headers.items():
|
||||
if k.lower().startswith('x-object-meta-'):
|
||||
new_req.headers[k] = v
|
||||
if new_req.headers.get('x-fresh-metadata', 'false').lower() \
|
||||
not in TRUE_VALUES:
|
||||
for k, v in source_resp.headers.items():
|
||||
if k.lower().startswith('x-object-meta-'):
|
||||
new_req.headers[k] = v
|
||||
for k, v in req.headers.items():
|
||||
if k.lower().startswith('x-object-meta-'):
|
||||
new_req.headers[k] = v
|
||||
req = new_req
|
||||
node_iter = self.iter_nodes(partition, nodes, self.app.object_ring)
|
||||
pile = GreenPile(len(nodes))
|
||||
@ -1431,6 +1443,8 @@ class BaseApplication(object):
|
||||
int(conf.get('recheck_account_existence', 60))
|
||||
self.allow_account_management = \
|
||||
conf.get('allow_account_management', 'false').lower() == 'true'
|
||||
self.post_as_copy = \
|
||||
conf.get('post_as_copy', 'true').lower() in TRUE_VALUES
|
||||
self.resellers_conf = ConfigParser()
|
||||
self.resellers_conf.read(os.path.join(swift_dir, 'resellers.conf'))
|
||||
self.object_ring = object_ring or \
|
||||
|
@ -668,7 +668,7 @@ class File(Base):
|
||||
|
||||
self.conn.make_request('POST', self.path, hdrs=headers, cfg=cfg)
|
||||
|
||||
if self.conn.response.status != 202:
|
||||
if self.conn.response.status not in (201, 202):
|
||||
raise ResponseError(self.conn.response)
|
||||
|
||||
return True
|
||||
|
@ -1032,7 +1032,7 @@ class TestFile(Base):
|
||||
self.assert_(file.write())
|
||||
self.assert_status(201)
|
||||
self.assert_(file.sync_metadata())
|
||||
self.assert_status(202)
|
||||
self.assert_status((201, 202))
|
||||
else:
|
||||
self.assertRaises(ResponseError, file.write)
|
||||
self.assert_status(400)
|
||||
@ -1245,7 +1245,7 @@ class TestFile(Base):
|
||||
|
||||
file.metadata = metadata
|
||||
self.assert_(file.sync_metadata())
|
||||
self.assert_status(202)
|
||||
self.assert_status((201, 202))
|
||||
|
||||
file = self.env.container.file(file.name)
|
||||
self.assert_(file.initialize())
|
||||
|
@ -20,7 +20,7 @@ from signal import SIGTERM
|
||||
from subprocess import Popen
|
||||
from time import sleep
|
||||
|
||||
from swift.common import client
|
||||
from swift.common import client, direct_client
|
||||
from test.probe.common import get_to_final_state, kill_pids, reset_environment
|
||||
|
||||
|
||||
@ -146,7 +146,8 @@ class TestAccountFailures(unittest.TestCase):
|
||||
sleep(2)
|
||||
# This is the earlier counts and bytes because the first node doesn't
|
||||
# have the newest udpates yet.
|
||||
headers, containers = client.get_account(self.url, self.token)
|
||||
headers, containers = \
|
||||
direct_client.direct_get_account(anodes[0], apart, self.account)
|
||||
self.assertEquals(headers['x-account-container-count'], '2')
|
||||
self.assertEquals(headers['x-account-object-count'], '1')
|
||||
self.assertEquals(headers['x-account-bytes-used'], '4')
|
||||
@ -167,7 +168,8 @@ class TestAccountFailures(unittest.TestCase):
|
||||
self.assert_(found2)
|
||||
|
||||
get_to_final_state()
|
||||
headers, containers = client.get_account(self.url, self.token)
|
||||
headers, containers = \
|
||||
direct_client.direct_get_account(anodes[0], apart, self.account)
|
||||
self.assertEquals(headers['x-account-container-count'], '1')
|
||||
self.assertEquals(headers['x-account-object-count'], '2')
|
||||
self.assertEquals(headers['x-account-bytes-used'], '9')
|
||||
|
@ -24,7 +24,7 @@ from uuid import uuid4
|
||||
import eventlet
|
||||
import sqlite3
|
||||
|
||||
from swift.common import client
|
||||
from swift.common import client, direct_client
|
||||
from swift.common.utils import hash_path, readconf
|
||||
|
||||
from test.probe.common import get_to_final_state, kill_pids, reset_environment
|
||||
@ -72,7 +72,8 @@ class TestContainerFailures(unittest.TestCase):
|
||||
# This okay because the first node hasn't got the update that the
|
||||
# object was deleted yet.
|
||||
self.assert_(object1 in [o['name'] for o in
|
||||
client.get_container(self.url, self.token, container)[1]])
|
||||
direct_client.direct_get_container(cnodes[0], cpart,
|
||||
self.account, container)[1]])
|
||||
|
||||
# Unfortunately, the following might pass or fail, depending on the
|
||||
# position of the account server associated with the first container
|
||||
@ -88,7 +89,8 @@ class TestContainerFailures(unittest.TestCase):
|
||||
client.put_object(self.url, self.token, container, object2, 'test')
|
||||
# First node still doesn't know object1 was deleted yet; this is okay.
|
||||
self.assert_(object1 in [o['name'] for o in
|
||||
client.get_container(self.url, self.token, container)[1]])
|
||||
direct_client.direct_get_container(cnodes[0], cpart,
|
||||
self.account, container)[1]])
|
||||
# And, of course, our new object2 exists.
|
||||
self.assert_(object2 in [o['name'] for o in
|
||||
client.get_container(self.url, self.token, container)[1]])
|
||||
@ -150,7 +152,8 @@ class TestContainerFailures(unittest.TestCase):
|
||||
# server has to indicate the container exists for the put to continue.
|
||||
client.put_object(self.url, self.token, container, object2, 'test')
|
||||
self.assert_(object1 not in [o['name'] for o in
|
||||
client.get_container(self.url, self.token, container)[1]])
|
||||
direct_client.direct_get_container(cnodes[0], cpart,
|
||||
self.account, container)[1]])
|
||||
# And, of course, our new object2 exists.
|
||||
self.assert_(object2 in [o['name'] for o in
|
||||
client.get_container(self.url, self.token, container)[1]])
|
||||
@ -201,7 +204,8 @@ class TestContainerFailures(unittest.TestCase):
|
||||
# This okay because the first node hasn't got the update that the
|
||||
# object was deleted yet.
|
||||
self.assert_(object1 in [o['name'] for o in
|
||||
client.get_container(self.url, self.token, container)[1]])
|
||||
direct_client.direct_get_container(cnodes[0], cpart,
|
||||
self.account, container)[1]])
|
||||
|
||||
# This fails because all three nodes have to indicate deletion before
|
||||
# we tell the user it worked. Since the first node 409s (it hasn't got
|
||||
@ -228,7 +232,8 @@ class TestContainerFailures(unittest.TestCase):
|
||||
client.put_object(self.url, self.token, container, object2, 'test')
|
||||
# First node still doesn't know object1 was deleted yet; this is okay.
|
||||
self.assert_(object1 in [o['name'] for o in
|
||||
client.get_container(self.url, self.token, container)[1]])
|
||||
direct_client.direct_get_container(cnodes[0], cpart,
|
||||
self.account, container)[1]])
|
||||
# And, of course, our new object2 exists.
|
||||
self.assert_(object2 in [o['name'] for o in
|
||||
client.get_container(self.url, self.token, container)[1]])
|
||||
@ -277,7 +282,8 @@ class TestContainerFailures(unittest.TestCase):
|
||||
self.assert_(container in [c['name'] for c in
|
||||
client.get_account(self.url, self.token)[1]])
|
||||
self.assert_(object1 not in [o['name'] for o in
|
||||
client.get_container(self.url, self.token, container)[1]])
|
||||
direct_client.direct_get_container(cnodes[0], cpart,
|
||||
self.account, container)[1]])
|
||||
|
||||
# This fails because all three nodes have to indicate deletion before
|
||||
# we tell the user it worked. Since the first node 409s (it hasn't got
|
||||
@ -303,7 +309,8 @@ class TestContainerFailures(unittest.TestCase):
|
||||
# server has to indicate the container exists for the put to continue.
|
||||
client.put_object(self.url, self.token, container, object2, 'test')
|
||||
self.assert_(object1 not in [o['name'] for o in
|
||||
client.get_container(self.url, self.token, container)[1]])
|
||||
direct_client.direct_get_container(cnodes[0], cpart,
|
||||
self.account, container)[1]])
|
||||
# And, of course, our new object2 exists.
|
||||
self.assert_(object2 in [o['name'] for o in
|
||||
client.get_container(self.url, self.token, container)[1]])
|
||||
|
@ -124,47 +124,49 @@ class TestObjectHandoff(unittest.TestCase):
|
||||
if not exc:
|
||||
raise Exception('Handoff object server still had test object')
|
||||
|
||||
kill(self.pids[self.port2server[onode['port']]], SIGTERM)
|
||||
client.post_object(self.url, self.token, container, obj,
|
||||
headers={'x-object-meta-probe': 'value'})
|
||||
oheaders = client.head_object(self.url, self.token, container, obj)
|
||||
if oheaders.get('x-object-meta-probe') != 'value':
|
||||
raise Exception('Metadata incorrect, was %s' % repr(oheaders))
|
||||
exc = False
|
||||
try:
|
||||
direct_client.direct_get_object(another_onode, opart, self.account,
|
||||
container, obj)
|
||||
except Exception:
|
||||
exc = True
|
||||
if not exc:
|
||||
raise Exception('Handoff server claimed it had the object when '
|
||||
'it should not have it')
|
||||
self.pids[self.port2server[onode['port']]] = Popen([
|
||||
'swift-object-server',
|
||||
'/etc/swift/object-server/%d.conf' %
|
||||
((onode['port'] - 6000) / 10)]).pid
|
||||
sleep(2)
|
||||
oheaders = direct_client.direct_get_object(onode, opart, self.account,
|
||||
container, obj)[0]
|
||||
if oheaders.get('x-object-meta-probe') == 'value':
|
||||
raise Exception('Previously downed object server had the new '
|
||||
'metadata when it should not have it')
|
||||
# Run the extra server last so it'll remove it's extra partition
|
||||
ps = []
|
||||
for n in onodes:
|
||||
ps.append(Popen(['swift-object-replicator',
|
||||
'/etc/swift/object-server/%d.conf' %
|
||||
((n['port'] - 6000) / 10), 'once']))
|
||||
for p in ps:
|
||||
p.wait()
|
||||
call(['swift-object-replicator',
|
||||
'/etc/swift/object-server/%d.conf' %
|
||||
((another_onode['port'] - 6000) / 10), 'once'])
|
||||
oheaders = direct_client.direct_get_object(onode, opart, self.account,
|
||||
container, obj)[0]
|
||||
if oheaders.get('x-object-meta-probe') != 'value':
|
||||
raise Exception(
|
||||
'Previously downed object server did not have the new metadata')
|
||||
# Because POST has changed to a COPY by default, POSTs will succeed on all up
|
||||
# nodes now if at least one up node has the object.
|
||||
# kill(self.pids[self.port2server[onode['port']]], SIGTERM)
|
||||
# client.post_object(self.url, self.token, container, obj,
|
||||
# headers={'x-object-meta-probe': 'value'})
|
||||
# oheaders = client.head_object(self.url, self.token, container, obj)
|
||||
# if oheaders.get('x-object-meta-probe') != 'value':
|
||||
# raise Exception('Metadata incorrect, was %s' % repr(oheaders))
|
||||
# exc = False
|
||||
# try:
|
||||
# direct_client.direct_get_object(another_onode, opart, self.account,
|
||||
# container, obj)
|
||||
# except Exception:
|
||||
# exc = True
|
||||
# if not exc:
|
||||
# raise Exception('Handoff server claimed it had the object when '
|
||||
# 'it should not have it')
|
||||
# self.pids[self.port2server[onode['port']]] = Popen([
|
||||
# 'swift-object-server',
|
||||
# '/etc/swift/object-server/%d.conf' %
|
||||
# ((onode['port'] - 6000) / 10)]).pid
|
||||
# sleep(2)
|
||||
# oheaders = direct_client.direct_get_object(onode, opart, self.account,
|
||||
# container, obj)[0]
|
||||
# if oheaders.get('x-object-meta-probe') == 'value':
|
||||
# raise Exception('Previously downed object server had the new '
|
||||
# 'metadata when it should not have it')
|
||||
# # Run the extra server last so it'll remove it's extra partition
|
||||
# ps = []
|
||||
# for n in onodes:
|
||||
# ps.append(Popen(['swift-object-replicator',
|
||||
# '/etc/swift/object-server/%d.conf' %
|
||||
# ((n['port'] - 6000) / 10), 'once']))
|
||||
# for p in ps:
|
||||
# p.wait()
|
||||
# call(['swift-object-replicator',
|
||||
# '/etc/swift/object-server/%d.conf' %
|
||||
# ((another_onode['port'] - 6000) / 10), 'once'])
|
||||
# oheaders = direct_client.direct_get_object(onode, opart, self.account,
|
||||
# container, obj)[0]
|
||||
# if oheaders.get('x-object-meta-probe') != 'value':
|
||||
# raise Exception(
|
||||
# 'Previously downed object server did not have the new metadata')
|
||||
|
||||
kill(self.pids[self.port2server[onode['port']]], SIGTERM)
|
||||
client.delete_object(self.url, self.token, container, obj)
|
||||
|
@ -925,6 +925,7 @@ class TestObjectController(unittest.TestCase):
|
||||
|
||||
def test_POST(self):
|
||||
with save_globals():
|
||||
self.app.post_as_copy = False
|
||||
controller = proxy_server.ObjectController(self.app, 'account',
|
||||
'container', 'object')
|
||||
|
||||
@ -945,6 +946,28 @@ class TestObjectController(unittest.TestCase):
|
||||
test_status_map((200, 200, 404, 500, 500), 503)
|
||||
test_status_map((200, 200, 404, 404, 404), 404)
|
||||
|
||||
def test_POST_as_copy(self):
|
||||
with save_globals():
|
||||
controller = proxy_server.ObjectController(self.app, 'account',
|
||||
'container', 'object')
|
||||
|
||||
def test_status_map(statuses, expected):
|
||||
proxy_server.http_connect = fake_http_connect(*statuses)
|
||||
self.app.memcache.store = {}
|
||||
req = Request.blank('/a/c/o', {}, headers={
|
||||
'Content-Type': 'foo/bar'})
|
||||
self.app.update_request(req)
|
||||
res = controller.POST(req)
|
||||
expected = str(expected)
|
||||
self.assertEquals(res.status[:len(expected)], expected)
|
||||
test_status_map((200, 200, 200, 200, 200, 202, 202, 202), 202)
|
||||
test_status_map((200, 200, 200, 200, 200, 202, 202, 500), 202)
|
||||
test_status_map((200, 200, 200, 200, 200, 202, 500, 500), 503)
|
||||
test_status_map((200, 200, 200, 200, 200, 202, 404, 500), 503)
|
||||
test_status_map((200, 200, 200, 200, 200, 202, 404, 404), 404)
|
||||
test_status_map((200, 200, 200, 200, 200, 404, 500, 500), 503)
|
||||
test_status_map((200, 200, 200, 200, 200, 404, 404, 404), 404)
|
||||
|
||||
def test_DELETE(self):
|
||||
with save_globals():
|
||||
controller = proxy_server.ObjectController(self.app, 'account',
|
||||
@ -1061,6 +1084,7 @@ class TestObjectController(unittest.TestCase):
|
||||
|
||||
def test_POST_meta_val_len(self):
|
||||
with save_globals():
|
||||
self.app.post_as_copy = False
|
||||
controller = proxy_server.ObjectController(self.app, 'account',
|
||||
'container', 'object')
|
||||
proxy_server.http_connect = \
|
||||
@ -1080,8 +1104,30 @@ class TestObjectController(unittest.TestCase):
|
||||
res = controller.POST(req)
|
||||
self.assertEquals(res.status_int, 400)
|
||||
|
||||
def test_POST_as_copy_meta_val_len(self):
|
||||
with save_globals():
|
||||
controller = proxy_server.ObjectController(self.app, 'account',
|
||||
'container', 'object')
|
||||
proxy_server.http_connect = \
|
||||
fake_http_connect(200, 200, 200, 200, 200, 202, 202, 202)
|
||||
# acct cont objc objc objc obj obj obj
|
||||
req = Request.blank('/a/c/o', {}, headers={
|
||||
'Content-Type': 'foo/bar',
|
||||
'X-Object-Meta-Foo': 'x' * 256})
|
||||
self.app.update_request(req)
|
||||
res = controller.POST(req)
|
||||
self.assertEquals(res.status_int, 202)
|
||||
proxy_server.http_connect = fake_http_connect(202, 202, 202)
|
||||
req = Request.blank('/a/c/o', {}, headers={
|
||||
'Content-Type': 'foo/bar',
|
||||
'X-Object-Meta-Foo': 'x' * 257})
|
||||
self.app.update_request(req)
|
||||
res = controller.POST(req)
|
||||
self.assertEquals(res.status_int, 400)
|
||||
|
||||
def test_POST_meta_key_len(self):
|
||||
with save_globals():
|
||||
self.app.post_as_copy = False
|
||||
controller = proxy_server.ObjectController(self.app, 'account',
|
||||
'container', 'object')
|
||||
proxy_server.http_connect = \
|
||||
@ -1101,6 +1147,27 @@ class TestObjectController(unittest.TestCase):
|
||||
res = controller.POST(req)
|
||||
self.assertEquals(res.status_int, 400)
|
||||
|
||||
def test_POST_as_copy_meta_key_len(self):
|
||||
with save_globals():
|
||||
controller = proxy_server.ObjectController(self.app, 'account',
|
||||
'container', 'object')
|
||||
proxy_server.http_connect = \
|
||||
fake_http_connect(200, 200, 200, 200, 200, 202, 202, 202)
|
||||
# acct cont objc objc objc obj obj obj
|
||||
req = Request.blank('/a/c/o', {}, headers={
|
||||
'Content-Type': 'foo/bar',
|
||||
('X-Object-Meta-' + 'x' * 128): 'x'})
|
||||
self.app.update_request(req)
|
||||
res = controller.POST(req)
|
||||
self.assertEquals(res.status_int, 202)
|
||||
proxy_server.http_connect = fake_http_connect(202, 202, 202)
|
||||
req = Request.blank('/a/c/o', {}, headers={
|
||||
'Content-Type': 'foo/bar',
|
||||
('X-Object-Meta-' + 'x' * 129): 'x'})
|
||||
self.app.update_request(req)
|
||||
res = controller.POST(req)
|
||||
self.assertEquals(res.status_int, 400)
|
||||
|
||||
def test_POST_meta_count(self):
|
||||
with save_globals():
|
||||
controller = proxy_server.ObjectController(self.app, 'account',
|
||||
@ -1375,7 +1442,8 @@ class TestObjectController(unittest.TestCase):
|
||||
self.assert_status_map(controller.HEAD, (200, 200, 200), 503)
|
||||
self.assert_('last_error' in controller.app.object_ring.devs[0])
|
||||
self.assert_status_map(controller.PUT, (200, 201, 201, 201), 503)
|
||||
self.assert_status_map(controller.POST, (200, 202, 202, 202), 503)
|
||||
self.assert_status_map(controller.POST,
|
||||
(200, 200, 200, 200, 202, 202, 202), 503)
|
||||
self.assert_status_map(controller.DELETE,
|
||||
(200, 204, 204, 204), 503)
|
||||
self.app.error_suppression_interval = -300
|
||||
@ -1468,18 +1536,41 @@ class TestObjectController(unittest.TestCase):
|
||||
|
||||
def test_PUT_POST_requires_container_exist(self):
|
||||
with save_globals():
|
||||
self.app.post_as_copy = False
|
||||
self.app.memcache = FakeMemcacheReturnsNone()
|
||||
controller = proxy_server.ObjectController(self.app, 'account',
|
||||
'container', 'object')
|
||||
|
||||
proxy_server.http_connect = \
|
||||
fake_http_connect(404, 404, 404, 200, 200, 200)
|
||||
fake_http_connect(200, 404, 404, 404, 200, 200, 200)
|
||||
req = Request.blank('/a/c/o', environ={'REQUEST_METHOD': 'PUT'})
|
||||
self.app.update_request(req)
|
||||
resp = controller.PUT(req)
|
||||
self.assertEquals(resp.status_int, 404)
|
||||
|
||||
proxy_server.http_connect = \
|
||||
fake_http_connect(404, 404, 404, 200, 200, 200)
|
||||
fake_http_connect(200, 404, 404, 404, 200, 200)
|
||||
req = Request.blank('/a/c/o', environ={'REQUEST_METHOD': 'POST'},
|
||||
headers={'Content-Type': 'text/plain'})
|
||||
self.app.update_request(req)
|
||||
resp = controller.POST(req)
|
||||
self.assertEquals(resp.status_int, 404)
|
||||
|
||||
def test_PUT_POST_as_copy_requires_container_exist(self):
|
||||
with save_globals():
|
||||
self.app.memcache = FakeMemcacheReturnsNone()
|
||||
controller = proxy_server.ObjectController(self.app, 'account',
|
||||
'container', 'object')
|
||||
proxy_server.http_connect = \
|
||||
fake_http_connect(200, 404, 404, 404, 200, 200, 200)
|
||||
req = Request.blank('/a/c/o', environ={'REQUEST_METHOD': 'PUT'})
|
||||
self.app.update_request(req)
|
||||
resp = controller.PUT(req)
|
||||
self.assertEquals(resp.status_int, 404)
|
||||
|
||||
proxy_server.http_connect = \
|
||||
fake_http_connect(200, 404, 404, 404, 200, 200, 200, 200, 200,
|
||||
200)
|
||||
req = Request.blank('/a/c/o', environ={'REQUEST_METHOD': 'POST'},
|
||||
headers={'Content-Type': 'text/plain'})
|
||||
self.app.update_request(req)
|
||||
@ -1599,8 +1690,10 @@ class TestObjectController(unittest.TestCase):
|
||||
'X-Copy-From': 'c/o'})
|
||||
self.app.update_request(req)
|
||||
proxy_server.http_connect = \
|
||||
fake_http_connect(200, 200, 200, 200, 200, 201, 201, 201)
|
||||
# acct cont acct cont objc obj obj obj
|
||||
fake_http_connect(200, 200, 200, 200, 200, 200, 200, 201, 201,
|
||||
201)
|
||||
# acct cont acct cont objc objc objc obj obj
|
||||
# obj
|
||||
self.app.memcache.store = {}
|
||||
resp = controller.PUT(req)
|
||||
self.assertEquals(resp.status_int, 201)
|
||||
@ -1612,8 +1705,8 @@ class TestObjectController(unittest.TestCase):
|
||||
'X-Copy-From': 'c/o'})
|
||||
self.app.update_request(req)
|
||||
proxy_server.http_connect = \
|
||||
fake_http_connect(200, 200, 200, 200, 200)
|
||||
# acct cont acct cont objc
|
||||
fake_http_connect(200, 200, 200, 200, 200, 200, 200)
|
||||
# acct cont acct cont objc objc objc
|
||||
self.app.memcache.store = {}
|
||||
resp = controller.PUT(req)
|
||||
self.assertEquals(resp.status_int, 400)
|
||||
@ -1624,8 +1717,10 @@ class TestObjectController(unittest.TestCase):
|
||||
'X-Copy-From': 'c/o/o2'})
|
||||
req.account = 'a'
|
||||
proxy_server.http_connect = \
|
||||
fake_http_connect(200, 200, 200, 200, 200, 201, 201, 201)
|
||||
# acct cont acct cont objc obj obj obj
|
||||
fake_http_connect(200, 200, 200, 200, 200, 200, 200, 201, 201,
|
||||
201)
|
||||
# acct cont acct cont objc objc objc obj obj
|
||||
# obj
|
||||
self.app.memcache.store = {}
|
||||
resp = controller.PUT(req)
|
||||
self.assertEquals(resp.status_int, 201)
|
||||
@ -1637,8 +1732,10 @@ class TestObjectController(unittest.TestCase):
|
||||
'X-Copy-From': 'c/o%20o2'})
|
||||
req.account = 'a'
|
||||
proxy_server.http_connect = \
|
||||
fake_http_connect(200, 200, 200, 200, 200, 201, 201, 201)
|
||||
# acct cont acct cont objc obj obj obj
|
||||
fake_http_connect(200, 200, 200, 200, 200, 200, 200, 201, 201,
|
||||
201)
|
||||
# acct cont acct cont objc objc objc obj obj
|
||||
# obj
|
||||
self.app.memcache.store = {}
|
||||
resp = controller.PUT(req)
|
||||
self.assertEquals(resp.status_int, 201)
|
||||
@ -1650,8 +1747,10 @@ class TestObjectController(unittest.TestCase):
|
||||
'X-Copy-From': '/c/o'})
|
||||
self.app.update_request(req)
|
||||
proxy_server.http_connect = \
|
||||
fake_http_connect(200, 200, 200, 200, 200, 201, 201, 201)
|
||||
# acct cont acct cont objc obj obj obj
|
||||
fake_http_connect(200, 200, 200, 200, 200, 200, 200, 201, 201,
|
||||
201)
|
||||
# acct cont acct cont objc objc objc obj obj
|
||||
# obj
|
||||
self.app.memcache.store = {}
|
||||
resp = controller.PUT(req)
|
||||
self.assertEquals(resp.status_int, 201)
|
||||
@ -1662,8 +1761,10 @@ class TestObjectController(unittest.TestCase):
|
||||
'X-Copy-From': '/c/o/o2'})
|
||||
req.account = 'a'
|
||||
proxy_server.http_connect = \
|
||||
fake_http_connect(200, 200, 200, 200, 200, 201, 201, 201)
|
||||
# acct cont acct cont objc obj obj obj
|
||||
fake_http_connect(200, 200, 200, 200, 200, 200, 200, 201, 201,
|
||||
201)
|
||||
# acct cont acct cont objc objc objc obj obj
|
||||
# obj
|
||||
self.app.memcache.store = {}
|
||||
resp = controller.PUT(req)
|
||||
self.assertEquals(resp.status_int, 201)
|
||||
@ -1723,8 +1824,8 @@ class TestObjectController(unittest.TestCase):
|
||||
'X-Object-Meta-Ours': 'okay'})
|
||||
self.app.update_request(req)
|
||||
proxy_server.http_connect = \
|
||||
fake_http_connect(200, 200, 200, 201, 201, 201)
|
||||
# acct cont objc obj obj obj
|
||||
fake_http_connect(200, 200, 200, 200, 200, 201, 201, 201)
|
||||
# acct cont objc objc objc obj obj obj
|
||||
self.app.memcache.store = {}
|
||||
resp = controller.PUT(req)
|
||||
self.assertEquals(resp.status_int, 201)
|
||||
@ -2652,6 +2753,7 @@ class TestObjectController(unittest.TestCase):
|
||||
called[0] = True
|
||||
return HTTPUnauthorized(request=req)
|
||||
with save_globals():
|
||||
self.app.post_as_copy = False
|
||||
proxy_server.http_connect = \
|
||||
fake_http_connect(200, 200, 201, 201, 201)
|
||||
controller = proxy_server.ObjectController(self.app, 'account',
|
||||
@ -2663,6 +2765,24 @@ class TestObjectController(unittest.TestCase):
|
||||
res = controller.POST(req)
|
||||
self.assert_(called[0])
|
||||
|
||||
def test_POST_as_copy_calls_authorize(self):
|
||||
called = [False]
|
||||
|
||||
def authorize(req):
|
||||
called[0] = True
|
||||
return HTTPUnauthorized(request=req)
|
||||
with save_globals():
|
||||
proxy_server.http_connect = \
|
||||
fake_http_connect(200, 200, 200, 200, 200, 201, 201, 201)
|
||||
controller = proxy_server.ObjectController(self.app, 'account',
|
||||
'container', 'object')
|
||||
req = Request.blank('/a/c/o', environ={'REQUEST_METHOD': 'POST'},
|
||||
headers={'Content-Length': '5'}, body='12345')
|
||||
req.environ['swift.authorize'] = authorize
|
||||
self.app.update_request(req)
|
||||
res = controller.POST(req)
|
||||
self.assert_(called[0])
|
||||
|
||||
def test_PUT_calls_authorize(self):
|
||||
called = [False]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user