2013-09-20 01:00:54 +08:00
|
|
|
# Copyright (c) 2010-2012 OpenStack Foundation
|
2010-07-12 17:03:45 -05:00
|
|
|
#
|
|
|
|
# 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.
|
|
|
|
|
Allow for multiple X-(Account|Container)-* headers.
When the number of account/container or container/object replicas are
different, Swift had a few misbehaviors. This commit fixes them.
* On an object PUT/POST/DELETE, if there were 3 object replicas and
only 2 container replicas, then only 2 requests would be made to
object servers. Now, 3 requests will be made, but the third won't
have any X-Container-* headers in it.
* On an object PUT/POST/DELETE, if there were 3 object replicas and 4
container replicas, then only 3/4 container servers would receive
immediate updates; the fourth would be ignored. Now one of the
object servers will receive multiple (comma-separated) values in the
X-Container-* headers and it will attempt to contact both of them.
One side effect is that multiple async_pendings may be written for
updates to the same object. They'll have differing timestamps,
though, so all but the newest will be deleted unread. To trigger
this behavior, you have to have more container replicas than object
replicas, 2 or more of the container servers must be down, and the
headers sent to one object server must reference 2 or more down
container servers; it's unlikely enough and the consequences are so
minor that it didn't seem worth fixing.
The situation with account/containers is analogous, only without the
async_pendings.
Change-Id: I98bc2de93fb6b2346d6de1d764213d7563653e8d
2012-12-12 17:47:04 -08:00
|
|
|
import operator
|
2010-07-12 17:03:45 -05:00
|
|
|
import os
|
2012-12-17 06:39:25 -05:00
|
|
|
import mock
|
2010-07-12 17:03:45 -05:00
|
|
|
import unittest
|
2013-03-20 19:26:45 -07:00
|
|
|
from contextlib import contextmanager
|
2010-07-12 17:03:45 -05:00
|
|
|
from shutil import rmtree
|
|
|
|
from StringIO import StringIO
|
2011-01-19 14:18:37 -06:00
|
|
|
from tempfile import mkdtemp
|
2013-07-23 14:54:51 -07:00
|
|
|
from xml.dom import minidom
|
2010-07-12 17:03:45 -05:00
|
|
|
|
2011-11-07 16:24:08 +00:00
|
|
|
from eventlet import spawn, Timeout, listen
|
2010-07-12 17:03:45 -05:00
|
|
|
import simplejson
|
|
|
|
|
Enhance log msg to report referer and user-agent
Enhance internally logged messages to report referer and user-agent.
Pass the referering URL and METHOD between internal servers (when
known), and set the user-agent to be the server type (obj-server,
container-server, proxy-server, obj-updater, obj-replicator,
container-updater, direct-client, etc.) with the process PID. In
conjunction with the transaction ID, it helps to track down which PID
from a given system was responsible for initiating the request and
what that server was working on to make this request.
This has been helpful in tracking down interactions between object,
container and account servers.
We also take things a bit further performaing a bit of refactoring to
consolidate calls to transfer_headers() now that we have a helper
method for constructing them.
Finally we performed further changes to avoid header key duplication
due to string literal header key values and the various objects
representing headers for requests and responses. See below for more
details.
====
Header Keys
There seems to be a bit of a problem with the case of the various
string literals used for header keys and the interchangable way
standard Python dictionaries, HeaderKeyDict() and HeaderEnvironProxy()
objects are used.
If one is not careful, a header object of some sort (one that does not
normalize its keys, and that is not necessarily a dictionary) can be
constructed containing header keys which differ only by the case of
their string literals. E.g.:
{ 'x-trans-id': '1234', 'X-Trans-Id': '5678' }
Such an object, when passed to http_connect() will result in an
on-the-wire header where the key values are merged together, comma
separated, that looks something like:
HTTP_X_TRANS_ID: 1234,5678
For some headers in some contexts, this is behavior is desirable. For
example, one can also use a list of tuples which enumerate the multiple
values a single header should have.
However, in almost all of the contexts used in the code base, this is
not desirable.
This behavior arises from a combination of factors:
1. Header strings are not constants and different lower-case and
title-case header strings values are used interchangably in the
code at times
It might be worth the effort to make a pass through the code to
stop using string literals and use constants instead, but there
are plusses and minuses to doing that, so this was not attempted
in this effort
2. HeaderEnvironProxy() objects report their keys in ".title()"
case, but normalize all other key references to the form
expected by the Request class's environ field
swob.Request.headers fields are HeaderEnvironProxy() objects.
3. HeaderKeyDict() objects report their keys in ".lower()" case,
and normalize all other key references to ".lower()" case
swob.Response.headers fields are HeaderKeyDict() objects.
Depending on which object is used and how it is used, one can end up
with such a mismatch.
This commit takes the following steps as a (PROPOSED) solution:
1. Change HeaderKeyDict() to normalize using ".title()" case to
match HeaderEnvironProxy()
2. Replace standard python dictionary objects with HeaderKeyDict()
objects where possible
This gives us an object that normalizes key references to avoid
fixing the code to normalize the string literals.
3. Fix up a few places to use title case string literals to match
the new defaults
Change-Id: Ied56a1df83ffac793ee85e796424d7d20f18f469
Signed-off-by: Peter Portante <peter.portante@redhat.com>
2012-11-15 16:34:45 -05:00
|
|
|
from swift.common.swob import Request, HeaderKeyDict
|
2013-03-20 19:26:45 -07:00
|
|
|
import swift.container
|
2010-07-12 17:03:45 -05:00
|
|
|
from swift.container import server as container_server
|
2013-10-07 12:10:31 +00:00
|
|
|
from swift.common.utils import normalize_timestamp, mkdirs, public, replication
|
2013-03-20 19:26:45 -07:00
|
|
|
from test.unit import fake_http_connect
|
|
|
|
|
|
|
|
|
|
|
|
@contextmanager
|
|
|
|
def save_globals():
|
|
|
|
orig_http_connect = getattr(swift.container.server, 'http_connect',
|
|
|
|
None)
|
|
|
|
try:
|
|
|
|
yield True
|
|
|
|
finally:
|
|
|
|
swift.container.server.http_connect = orig_http_connect
|
2010-07-12 17:03:45 -05:00
|
|
|
|
|
|
|
|
|
|
|
class TestContainerController(unittest.TestCase):
|
2013-08-31 22:36:58 -04:00
|
|
|
"""Test swift.container.server.ContainerController"""
|
2010-07-12 17:03:45 -05:00
|
|
|
def setUp(self):
|
2013-08-31 22:36:58 -04:00
|
|
|
"""Set up for testing swift.object_server.ObjectController"""
|
2011-01-19 16:19:43 -06:00
|
|
|
self.testdir = os.path.join(mkdtemp(),
|
|
|
|
'tmp_test_object_server_ObjectController')
|
2010-07-12 17:03:45 -05:00
|
|
|
mkdirs(self.testdir)
|
|
|
|
rmtree(self.testdir)
|
|
|
|
mkdirs(os.path.join(self.testdir, 'sda1'))
|
|
|
|
mkdirs(os.path.join(self.testdir, 'sda1', 'tmp'))
|
|
|
|
self.controller = container_server.ContainerController(
|
|
|
|
{'devices': self.testdir, 'mount_check': 'false'})
|
|
|
|
|
|
|
|
def tearDown(self):
|
2013-08-31 22:36:58 -04:00
|
|
|
"""Tear down for testing swift.object_server.ObjectController"""
|
2011-01-24 17:12:38 -08:00
|
|
|
rmtree(os.path.dirname(self.testdir), ignore_errors=1)
|
2010-07-12 17:03:45 -05:00
|
|
|
|
2010-09-02 21:50:16 -07:00
|
|
|
def test_acl_container(self):
|
|
|
|
# Ensure no acl by default
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT'},
|
2010-09-02 21:50:16 -07:00
|
|
|
headers={'X-Timestamp': '0'})
|
2013-09-01 12:50:07 -04:00
|
|
|
resp = req.get_response(self.controller)
|
|
|
|
self.assert_(resp.status.startswith('201'))
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
response = req.get_response(self.controller)
|
2010-09-02 21:50:16 -07:00
|
|
|
self.assert_(response.status.startswith('204'))
|
|
|
|
self.assert_('x-container-read' not in response.headers)
|
|
|
|
self.assert_('x-container-write' not in response.headers)
|
|
|
|
# Ensure POSTing acls works
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'POST'},
|
2010-09-08 22:37:27 -07:00
|
|
|
headers={'X-Timestamp': '1', 'X-Container-Read': '.r:*',
|
2010-09-02 21:50:16 -07:00
|
|
|
'X-Container-Write': 'account:user'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-09-01 12:50:07 -04:00
|
|
|
self.assert_(resp.status.startswith('204'))
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
response = req.get_response(self.controller)
|
2010-09-02 21:50:16 -07:00
|
|
|
self.assert_(response.status.startswith('204'))
|
2010-09-08 22:37:27 -07:00
|
|
|
self.assertEquals(response.headers.get('x-container-read'), '.r:*')
|
2010-09-02 21:50:16 -07:00
|
|
|
self.assertEquals(response.headers.get('x-container-write'),
|
|
|
|
'account:user')
|
|
|
|
# Ensure we can clear acls on POST
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'POST'},
|
2010-09-02 21:50:16 -07:00
|
|
|
headers={'X-Timestamp': '3', 'X-Container-Read': '',
|
|
|
|
'X-Container-Write': ''})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-09-01 12:50:07 -04:00
|
|
|
self.assert_(resp.status.startswith('204'))
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
response = req.get_response(self.controller)
|
2010-09-02 21:50:16 -07:00
|
|
|
self.assert_(response.status.startswith('204'))
|
|
|
|
self.assert_('x-container-read' not in response.headers)
|
|
|
|
self.assert_('x-container-write' not in response.headers)
|
|
|
|
# Ensure PUTing acls works
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c2', environ={'REQUEST_METHOD': 'PUT'},
|
2010-09-08 22:37:27 -07:00
|
|
|
headers={'X-Timestamp': '4', 'X-Container-Read': '.r:*',
|
2010-09-02 21:50:16 -07:00
|
|
|
'X-Container-Write': 'account:user'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-09-01 12:50:07 -04:00
|
|
|
self.assert_(resp.status.startswith('201'))
|
2010-09-02 21:50:16 -07:00
|
|
|
req = Request.blank('/sda1/p/a/c2', environ={'REQUEST_METHOD': 'HEAD'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
response = req.get_response(self.controller)
|
2010-09-02 21:50:16 -07:00
|
|
|
self.assert_(response.status.startswith('204'))
|
2010-09-08 22:37:27 -07:00
|
|
|
self.assertEquals(response.headers.get('x-container-read'), '.r:*')
|
2010-09-02 21:50:16 -07:00
|
|
|
self.assertEquals(response.headers.get('x-container-write'),
|
|
|
|
'account:user')
|
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def test_HEAD(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
2010-07-12 17:03:45 -05:00
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
req.get_response(self.controller)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD',
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
|
|
|
response = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assert_(response.status.startswith('204'))
|
|
|
|
self.assertEquals(int(response.headers['x-container-bytes-used']), 0)
|
|
|
|
self.assertEquals(int(response.headers['x-container-object-count']), 0)
|
2013-09-01 15:10:39 -04:00
|
|
|
req2 = Request.blank(
|
|
|
|
'/sda1/p/a/c/o', environ={
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
'REQUEST_METHOD': 'PUT',
|
2013-03-20 19:26:45 -07:00
|
|
|
'HTTP_X_TIMESTAMP': '1', 'HTTP_X_SIZE': 42,
|
|
|
|
'HTTP_X_CONTENT_TYPE': 'text/plain', 'HTTP_X_ETAG': 'x'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
req2.get_response(self.controller)
|
|
|
|
response = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(int(response.headers['x-container-bytes-used']), 42)
|
|
|
|
self.assertEquals(int(response.headers['x-container-object-count']), 1)
|
|
|
|
|
|
|
|
def test_HEAD_not_found(self):
|
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 404)
|
|
|
|
|
2013-03-20 19:26:45 -07:00
|
|
|
def test_HEAD_invalid_partition(self):
|
|
|
|
req = Request.blank('/sda1/./a/c', environ={'REQUEST_METHOD': 'HEAD',
|
|
|
|
'HTTP_X_TIMESTAMP': '1'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 400)
|
|
|
|
|
|
|
|
def test_HEAD_insufficient_storage(self):
|
|
|
|
self.controller = container_server.ContainerController(
|
|
|
|
{'devices': self.testdir})
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda-null/p/a/c', environ={'REQUEST_METHOD': 'HEAD',
|
|
|
|
'HTTP_X_TIMESTAMP': '1'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 507)
|
|
|
|
|
|
|
|
def test_HEAD_invalid_content_type(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'},
|
|
|
|
headers={'Accept': 'application/plain'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 406)
|
|
|
|
|
2013-06-12 15:50:13 -07:00
|
|
|
def test_HEAD_invalid_format(self):
|
|
|
|
format = '%D1%BD%8A9' # invalid UTF-8; should be %E1%BD%8A9 (E -> D)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c?format=' + format,
|
|
|
|
environ={'REQUEST_METHOD': 'HEAD'})
|
2013-07-24 12:55:25 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-06-12 15:50:13 -07:00
|
|
|
self.assertEquals(resp.status_int, 400)
|
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def test_PUT(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
2010-07-12 17:03:45 -05:00
|
|
|
'HTTP_X_TIMESTAMP': '1'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
2010-07-12 17:03:45 -05:00
|
|
|
'HTTP_X_TIMESTAMP': '2'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 202)
|
|
|
|
|
|
|
|
def test_PUT_obj_not_found(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c/o', environ={'REQUEST_METHOD': 'PUT'},
|
2010-07-12 17:03:45 -05:00
|
|
|
headers={'X-Timestamp': '1', 'X-Size': '0',
|
|
|
|
'X-Content-Type': 'text/plain', 'X-ETag': 'e'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 404)
|
|
|
|
|
2010-08-10 12:18:15 -07:00
|
|
|
def test_PUT_GET_metadata(self):
|
|
|
|
# Set metadata header
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT'},
|
2010-08-10 12:18:15 -07:00
|
|
|
headers={'X-Timestamp': normalize_timestamp(1),
|
|
|
|
'X-Container-Meta-Test': 'Value'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
2013-08-16 16:24:00 -07:00
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'GET'})
|
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
self.assertEquals(resp.headers.get('x-container-meta-test'), 'Value')
|
2010-08-16 15:30:27 -07:00
|
|
|
# Set another metadata header, ensuring old one doesn't disappear
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'POST'},
|
2010-08-16 15:30:27 -07:00
|
|
|
headers={'X-Timestamp': normalize_timestamp(1),
|
|
|
|
'X-Container-Meta-Test2': 'Value2'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-08-16 15:30:27 -07:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
2013-08-16 16:24:00 -07:00
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'GET'})
|
|
|
|
resp = req.get_response(self.controller)
|
2010-08-16 15:30:27 -07:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
self.assertEquals(resp.headers.get('x-container-meta-test'), 'Value')
|
|
|
|
self.assertEquals(resp.headers.get('x-container-meta-test2'), 'Value2')
|
2010-08-10 12:18:15 -07:00
|
|
|
# Update metadata header
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT'},
|
2010-08-10 12:18:15 -07:00
|
|
|
headers={'X-Timestamp': normalize_timestamp(3),
|
|
|
|
'X-Container-Meta-Test': 'New Value'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 202)
|
2013-08-16 16:24:00 -07:00
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'GET'})
|
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
self.assertEquals(resp.headers.get('x-container-meta-test'),
|
|
|
|
'New Value')
|
|
|
|
# Send old update to metadata header
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT'},
|
2010-08-10 12:18:15 -07:00
|
|
|
headers={'X-Timestamp': normalize_timestamp(2),
|
|
|
|
'X-Container-Meta-Test': 'Old Value'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 202)
|
2013-08-16 16:24:00 -07:00
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'GET'})
|
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
self.assertEquals(resp.headers.get('x-container-meta-test'),
|
|
|
|
'New Value')
|
|
|
|
# Remove metadata header (by setting it to empty)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT'},
|
2010-08-10 12:18:15 -07:00
|
|
|
headers={'X-Timestamp': normalize_timestamp(4),
|
|
|
|
'X-Container-Meta-Test': ''})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 202)
|
2013-08-16 16:24:00 -07:00
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'GET'})
|
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
self.assert_('x-container-meta-test' not in resp.headers)
|
|
|
|
|
2013-03-20 19:26:45 -07:00
|
|
|
def test_PUT_invalid_partition(self):
|
|
|
|
req = Request.blank('/sda1/./a/c', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '1'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 400)
|
|
|
|
|
|
|
|
def test_PUT_timestamp_not_float(self):
|
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
2013-08-16 16:24:00 -07:00
|
|
|
req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT'},
|
|
|
|
headers={'X-Timestamp': 'not-float'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 400)
|
|
|
|
|
|
|
|
def test_PUT_insufficient_storage(self):
|
|
|
|
self.controller = container_server.ContainerController(
|
|
|
|
{'devices': self.testdir})
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda-null/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '1'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 507)
|
|
|
|
|
2010-08-10 12:18:15 -07:00
|
|
|
def test_POST_HEAD_metadata(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT'},
|
2010-08-10 12:18:15 -07:00
|
|
|
headers={'X-Timestamp': normalize_timestamp(1)})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
|
|
|
# Set metadata header
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'POST'},
|
2010-08-10 12:18:15 -07:00
|
|
|
headers={'X-Timestamp': normalize_timestamp(1),
|
|
|
|
'X-Container-Meta-Test': 'Value'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
self.assertEquals(resp.headers.get('x-container-meta-test'), 'Value')
|
|
|
|
# Update metadata header
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'POST'},
|
2010-08-10 12:18:15 -07:00
|
|
|
headers={'X-Timestamp': normalize_timestamp(3),
|
|
|
|
'X-Container-Meta-Test': 'New Value'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
self.assertEquals(resp.headers.get('x-container-meta-test'),
|
|
|
|
'New Value')
|
|
|
|
# Send old update to metadata header
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'POST'},
|
2010-08-10 12:18:15 -07:00
|
|
|
headers={'X-Timestamp': normalize_timestamp(2),
|
|
|
|
'X-Container-Meta-Test': 'Old Value'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
self.assertEquals(resp.headers.get('x-container-meta-test'),
|
|
|
|
'New Value')
|
|
|
|
# Remove metadata header (by setting it to empty)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'POST'},
|
2010-08-10 12:18:15 -07:00
|
|
|
headers={'X-Timestamp': normalize_timestamp(4),
|
|
|
|
'X-Container-Meta-Test': ''})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-08-10 12:18:15 -07:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
self.assert_('x-container-meta-test' not in resp.headers)
|
|
|
|
|
2013-03-20 19:26:45 -07:00
|
|
|
def test_POST_invalid_partition(self):
|
|
|
|
req = Request.blank('/sda1/./a/c', environ={'REQUEST_METHOD': 'POST',
|
|
|
|
'HTTP_X_TIMESTAMP': '1'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 400)
|
|
|
|
|
|
|
|
def test_POST_timestamp_not_float(self):
|
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
2013-08-16 16:24:00 -07:00
|
|
|
req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'POST'},
|
|
|
|
headers={'X-Timestamp': 'not-float'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 400)
|
|
|
|
|
|
|
|
def test_POST_insufficient_storage(self):
|
|
|
|
self.controller = container_server.ContainerController(
|
|
|
|
{'devices': self.testdir})
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda-null/p/a/c', environ={'REQUEST_METHOD': 'POST',
|
|
|
|
'HTTP_X_TIMESTAMP': '1'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 507)
|
|
|
|
|
|
|
|
def test_POST_invalid_container_sync_to(self):
|
|
|
|
self.controller = container_server.ContainerController(
|
|
|
|
{'devices': self.testdir})
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda-null/p/a/c', environ={'REQUEST_METHOD': 'POST',
|
|
|
|
'HTTP_X_TIMESTAMP': '1'},
|
|
|
|
headers={'x-container-sync-to': '192.168.0.1'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 400)
|
|
|
|
|
|
|
|
def test_POST_after_DELETE_not_found(self):
|
|
|
|
req = Request.blank('/sda1/p/a/c',
|
|
|
|
environ={'REQUEST_METHOD': 'PUT'},
|
|
|
|
headers={'X-Timestamp': '1'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
req = Request.blank('/sda1/p/a/c',
|
|
|
|
environ={'REQUEST_METHOD': 'DELETE'},
|
|
|
|
headers={'X-Timestamp': '2'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
req = Request.blank('/sda1/p/a/c/',
|
|
|
|
environ={'REQUEST_METHOD': 'POST'},
|
|
|
|
headers={'X-Timestamp': '3'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 404)
|
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def test_DELETE_obj_not_found(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c/o',
|
|
|
|
environ={'REQUEST_METHOD': 'DELETE'},
|
|
|
|
headers={'X-Timestamp': '1'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 404)
|
|
|
|
|
2013-03-20 19:26:45 -07:00
|
|
|
def test_DELETE_container_not_found(self):
|
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'DELETE',
|
|
|
|
'HTTP_X_TIMESTAMP': '1'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 404)
|
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def test_PUT_utf8(self):
|
|
|
|
snowman = u'\u2603'
|
|
|
|
container_name = snowman.encode('utf-8')
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/%s' % container_name, environ={
|
2013-03-20 19:26:45 -07:00
|
|
|
'REQUEST_METHOD': 'PUT',
|
2010-07-12 17:03:45 -05:00
|
|
|
'HTTP_X_TIMESTAMP': '1'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
|
|
|
|
2013-03-20 19:26:45 -07:00
|
|
|
def test_account_update_mismatched_host_device(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2013-03-20 19:26:45 -07:00
|
|
|
environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '1'},
|
|
|
|
headers={'X-Timestamp': '0000000001.00000',
|
|
|
|
'X-Account-Host': '127.0.0.1:0',
|
|
|
|
'X-Account-Partition': '123',
|
|
|
|
'X-Account-Device': 'sda1,sda2'})
|
|
|
|
broker = self.controller._get_container_broker('sda1', 'p', 'a', 'c')
|
|
|
|
resp = self.controller.account_update(req, 'a', 'c', broker)
|
|
|
|
self.assertEquals(resp.status_int, 400)
|
|
|
|
|
|
|
|
def test_account_update_account_override_deleted(self):
|
|
|
|
bindsock = listen(('127.0.0.1', 0))
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
|
|
|
environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '1'},
|
|
|
|
headers={'X-Timestamp': '0000000001.00000',
|
|
|
|
'X-Account-Host': '%s:%s' %
|
|
|
|
bindsock.getsockname(),
|
|
|
|
'X-Account-Partition': '123',
|
|
|
|
'X-Account-Device': 'sda1',
|
|
|
|
'X-Account-Override-Deleted': 'yes'})
|
2013-03-20 19:26:45 -07:00
|
|
|
with save_globals():
|
|
|
|
new_connect = fake_http_connect(200, count=123)
|
|
|
|
swift.container.server.http_connect = new_connect
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def test_PUT_account_update(self):
|
|
|
|
bindsock = listen(('127.0.0.1', 0))
|
2013-03-20 19:26:45 -07:00
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def accept(return_code, expected_timestamp):
|
|
|
|
try:
|
|
|
|
with Timeout(3):
|
|
|
|
sock, addr = bindsock.accept()
|
|
|
|
inc = sock.makefile('rb')
|
|
|
|
out = sock.makefile('wb')
|
|
|
|
out.write('HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n' %
|
|
|
|
return_code)
|
|
|
|
out.flush()
|
|
|
|
self.assertEquals(inc.readline(),
|
|
|
|
'PUT /sda1/123/a/c HTTP/1.1\r\n')
|
|
|
|
headers = {}
|
|
|
|
line = inc.readline()
|
|
|
|
while line and line != '\r\n':
|
|
|
|
headers[line.split(':')[0].lower()] = \
|
|
|
|
line.split(':')[1].strip()
|
|
|
|
line = inc.readline()
|
|
|
|
self.assertEquals(headers['x-put-timestamp'],
|
|
|
|
expected_timestamp)
|
2013-08-28 21:16:08 +02:00
|
|
|
except BaseException as err:
|
2010-07-12 17:03:45 -05:00
|
|
|
return err
|
|
|
|
return None
|
2013-03-20 19:26:45 -07:00
|
|
|
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'PUT'},
|
|
|
|
headers={'X-Timestamp': '0000000001.00000',
|
|
|
|
'X-Account-Host': '%s:%s' % bindsock.getsockname(),
|
|
|
|
'X-Account-Partition': '123',
|
|
|
|
'X-Account-Device': 'sda1'})
|
|
|
|
event = spawn(accept, 201, '0000000001.00000')
|
|
|
|
try:
|
|
|
|
with Timeout(3):
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
|
|
|
finally:
|
|
|
|
err = event.wait()
|
|
|
|
if err:
|
|
|
|
raise Exception(err)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'DELETE'},
|
|
|
|
headers={'X-Timestamp': '2'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'PUT'},
|
|
|
|
headers={'X-Timestamp': '0000000003.00000',
|
|
|
|
'X-Account-Host': '%s:%s' % bindsock.getsockname(),
|
|
|
|
'X-Account-Partition': '123',
|
|
|
|
'X-Account-Device': 'sda1'})
|
|
|
|
event = spawn(accept, 404, '0000000003.00000')
|
|
|
|
try:
|
|
|
|
with Timeout(3):
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 404)
|
|
|
|
finally:
|
|
|
|
err = event.wait()
|
|
|
|
if err:
|
|
|
|
raise Exception(err)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'PUT'},
|
|
|
|
headers={'X-Timestamp': '0000000005.00000',
|
|
|
|
'X-Account-Host': '%s:%s' % bindsock.getsockname(),
|
|
|
|
'X-Account-Partition': '123',
|
|
|
|
'X-Account-Device': 'sda1'})
|
|
|
|
event = spawn(accept, 503, '0000000005.00000')
|
|
|
|
got_exc = False
|
|
|
|
try:
|
|
|
|
with Timeout(3):
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-08-28 21:16:08 +02:00
|
|
|
except BaseException as err:
|
2010-07-12 17:03:45 -05:00
|
|
|
got_exc = True
|
|
|
|
finally:
|
|
|
|
err = event.wait()
|
|
|
|
if err:
|
|
|
|
raise Exception(err)
|
|
|
|
self.assert_(not got_exc)
|
|
|
|
|
2011-07-14 20:07:45 +00:00
|
|
|
def test_PUT_reset_container_sync(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT'},
|
2011-07-14 20:07:45 +00:00
|
|
|
headers={'x-timestamp': '1',
|
|
|
|
'x-container-sync-to': 'http://127.0.0.1:12345/v1/a/c'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2011-07-14 20:07:45 +00:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
|
|
|
db = self.controller._get_container_broker('sda1', 'p', 'a', 'c')
|
|
|
|
info = db.get_info()
|
|
|
|
self.assertEquals(info['x_container_sync_point1'], -1)
|
|
|
|
self.assertEquals(info['x_container_sync_point2'], -1)
|
|
|
|
db.set_x_container_sync_points(123, 456)
|
|
|
|
info = db.get_info()
|
|
|
|
self.assertEquals(info['x_container_sync_point1'], 123)
|
|
|
|
self.assertEquals(info['x_container_sync_point2'], 456)
|
|
|
|
# Set to same value
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT'},
|
2011-07-14 20:07:45 +00:00
|
|
|
headers={'x-timestamp': '1',
|
|
|
|
'x-container-sync-to': 'http://127.0.0.1:12345/v1/a/c'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2011-07-14 20:07:45 +00:00
|
|
|
self.assertEquals(resp.status_int, 202)
|
|
|
|
db = self.controller._get_container_broker('sda1', 'p', 'a', 'c')
|
|
|
|
info = db.get_info()
|
|
|
|
self.assertEquals(info['x_container_sync_point1'], 123)
|
|
|
|
self.assertEquals(info['x_container_sync_point2'], 456)
|
|
|
|
# Set to new value
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT'},
|
2011-07-14 20:07:45 +00:00
|
|
|
headers={'x-timestamp': '1',
|
|
|
|
'x-container-sync-to': 'http://127.0.0.1:12345/v1/a/c2'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2011-07-14 20:07:45 +00:00
|
|
|
self.assertEquals(resp.status_int, 202)
|
|
|
|
db = self.controller._get_container_broker('sda1', 'p', 'a', 'c')
|
|
|
|
info = db.get_info()
|
|
|
|
self.assertEquals(info['x_container_sync_point1'], -1)
|
|
|
|
self.assertEquals(info['x_container_sync_point2'], -1)
|
|
|
|
|
|
|
|
def test_POST_reset_container_sync(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT'},
|
2011-07-14 20:07:45 +00:00
|
|
|
headers={'x-timestamp': '1',
|
|
|
|
'x-container-sync-to': 'http://127.0.0.1:12345/v1/a/c'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2011-07-14 20:07:45 +00:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
|
|
|
db = self.controller._get_container_broker('sda1', 'p', 'a', 'c')
|
|
|
|
info = db.get_info()
|
|
|
|
self.assertEquals(info['x_container_sync_point1'], -1)
|
|
|
|
self.assertEquals(info['x_container_sync_point2'], -1)
|
|
|
|
db.set_x_container_sync_points(123, 456)
|
|
|
|
info = db.get_info()
|
|
|
|
self.assertEquals(info['x_container_sync_point1'], 123)
|
|
|
|
self.assertEquals(info['x_container_sync_point2'], 456)
|
|
|
|
# Set to same value
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'POST'},
|
2011-07-14 20:07:45 +00:00
|
|
|
headers={'x-timestamp': '1',
|
|
|
|
'x-container-sync-to': 'http://127.0.0.1:12345/v1/a/c'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2011-07-14 20:07:45 +00:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
db = self.controller._get_container_broker('sda1', 'p', 'a', 'c')
|
|
|
|
info = db.get_info()
|
|
|
|
self.assertEquals(info['x_container_sync_point1'], 123)
|
|
|
|
self.assertEquals(info['x_container_sync_point2'], 456)
|
|
|
|
# Set to new value
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'POST'},
|
2011-07-14 20:07:45 +00:00
|
|
|
headers={'x-timestamp': '1',
|
|
|
|
'x-container-sync-to': 'http://127.0.0.1:12345/v1/a/c2'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2011-07-14 20:07:45 +00:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
db = self.controller._get_container_broker('sda1', 'p', 'a', 'c')
|
|
|
|
info = db.get_info()
|
|
|
|
self.assertEquals(info['x_container_sync_point1'], -1)
|
|
|
|
self.assertEquals(info['x_container_sync_point2'], -1)
|
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def test_DELETE(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'PUT'}, headers={'X-Timestamp': '1'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'DELETE'}, headers={'X-Timestamp': '2'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'GET'}, headers={'X-Timestamp': '3'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 404)
|
|
|
|
|
|
|
|
def test_DELETE_not_found(self):
|
|
|
|
# Even if the container wasn't previously heard of, the container
|
|
|
|
# server will accept the delete and replicate it to where it belongs
|
|
|
|
# later.
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'DELETE', 'HTTP_X_TIMESTAMP': '1'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 404)
|
|
|
|
|
|
|
|
def test_DELETE_object(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'PUT'}, headers={'X-Timestamp': '2'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c/o',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'PUT', 'HTTP_X_TIMESTAMP': '0',
|
|
|
|
'HTTP_X_SIZE': 1, 'HTTP_X_CONTENT_TYPE': 'text/plain',
|
|
|
|
'HTTP_X_ETAG': 'x'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'DELETE'}, headers={'X-Timestamp': '3'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 409)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c/o',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'DELETE'}, headers={'X-Timestamp': '4'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'DELETE'}, headers={'X-Timestamp': '5'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'GET'}, headers={'X-Timestamp': '6'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 404)
|
|
|
|
|
|
|
|
def test_DELETE_account_update(self):
|
|
|
|
bindsock = listen(('127.0.0.1', 0))
|
2013-03-20 19:26:45 -07:00
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def accept(return_code, expected_timestamp):
|
|
|
|
try:
|
|
|
|
with Timeout(3):
|
|
|
|
sock, addr = bindsock.accept()
|
|
|
|
inc = sock.makefile('rb')
|
|
|
|
out = sock.makefile('wb')
|
|
|
|
out.write('HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n' %
|
|
|
|
return_code)
|
|
|
|
out.flush()
|
|
|
|
self.assertEquals(inc.readline(),
|
|
|
|
'PUT /sda1/123/a/c HTTP/1.1\r\n')
|
|
|
|
headers = {}
|
|
|
|
line = inc.readline()
|
|
|
|
while line and line != '\r\n':
|
|
|
|
headers[line.split(':')[0].lower()] = \
|
|
|
|
line.split(':')[1].strip()
|
|
|
|
line = inc.readline()
|
|
|
|
self.assertEquals(headers['x-delete-timestamp'],
|
|
|
|
expected_timestamp)
|
2013-08-28 21:16:08 +02:00
|
|
|
except BaseException as err:
|
2010-07-12 17:03:45 -05:00
|
|
|
return err
|
|
|
|
return None
|
2013-03-20 19:26:45 -07:00
|
|
|
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'PUT'}, headers={'X-Timestamp': '1'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'DELETE'},
|
|
|
|
headers={'X-Timestamp': '0000000002.00000',
|
|
|
|
'X-Account-Host': '%s:%s' % bindsock.getsockname(),
|
|
|
|
'X-Account-Partition': '123',
|
|
|
|
'X-Account-Device': 'sda1'})
|
|
|
|
event = spawn(accept, 204, '0000000002.00000')
|
|
|
|
try:
|
|
|
|
with Timeout(3):
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
finally:
|
|
|
|
err = event.wait()
|
|
|
|
if err:
|
|
|
|
raise Exception(err)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'PUT', 'HTTP_X_TIMESTAMP': '2'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'DELETE'},
|
|
|
|
headers={'X-Timestamp': '0000000003.00000',
|
|
|
|
'X-Account-Host': '%s:%s' % bindsock.getsockname(),
|
|
|
|
'X-Account-Partition': '123',
|
|
|
|
'X-Account-Device': 'sda1'})
|
|
|
|
event = spawn(accept, 404, '0000000003.00000')
|
|
|
|
try:
|
|
|
|
with Timeout(3):
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 404)
|
|
|
|
finally:
|
|
|
|
err = event.wait()
|
|
|
|
if err:
|
|
|
|
raise Exception(err)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'PUT', 'HTTP_X_TIMESTAMP': '4'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
2010-07-12 17:03:45 -05:00
|
|
|
environ={'REQUEST_METHOD': 'DELETE'},
|
|
|
|
headers={'X-Timestamp': '0000000005.00000',
|
|
|
|
'X-Account-Host': '%s:%s' % bindsock.getsockname(),
|
|
|
|
'X-Account-Partition': '123',
|
|
|
|
'X-Account-Device': 'sda1'})
|
|
|
|
event = spawn(accept, 503, '0000000005.00000')
|
|
|
|
got_exc = False
|
|
|
|
try:
|
|
|
|
with Timeout(3):
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-08-28 21:16:08 +02:00
|
|
|
except BaseException as err:
|
2010-07-12 17:03:45 -05:00
|
|
|
got_exc = True
|
|
|
|
finally:
|
|
|
|
err = event.wait()
|
|
|
|
if err:
|
|
|
|
raise Exception(err)
|
|
|
|
self.assert_(not got_exc)
|
|
|
|
|
2013-03-20 19:26:45 -07:00
|
|
|
def test_DELETE_invalid_partition(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/./a/c', environ={'REQUEST_METHOD': 'DELETE',
|
|
|
|
'HTTP_X_TIMESTAMP': '1'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 400)
|
|
|
|
|
|
|
|
def test_DELETE_timestamp_not_float(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
2013-08-16 16:24:00 -07:00
|
|
|
req.get_response(self.controller)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'DELETE'},
|
|
|
|
headers={'X-Timestamp': 'not-float'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 400)
|
|
|
|
|
|
|
|
def test_DELETE_insufficient_storage(self):
|
|
|
|
self.controller = container_server.ContainerController(
|
|
|
|
{'devices': self.testdir})
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda-null/p/a/c', environ={'REQUEST_METHOD': 'DELETE',
|
|
|
|
'HTTP_X_TIMESTAMP': '1'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 507)
|
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def test_GET_over_limit(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c?limit=%d' %
|
2010-07-12 17:03:45 -05:00
|
|
|
(container_server.CONTAINER_LISTING_LIMIT + 1),
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 412)
|
|
|
|
|
2010-10-29 01:23:01 +00:00
|
|
|
def test_GET_json(self):
|
2010-07-12 17:03:45 -05:00
|
|
|
# make a container
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/jsonc', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
# test an empty container
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/jsonc?format=json',
|
2010-10-29 10:28:19 +00:00
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-10-29 10:28:19 +00:00
|
|
|
self.assertEquals(resp.status_int, 200)
|
2013-05-28 07:22:42 -07:00
|
|
|
self.assertEquals(simplejson.loads(resp.body), [])
|
2010-07-12 17:03:45 -05:00
|
|
|
# fill the container
|
|
|
|
for i in range(3):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/jsonc/%s' % i, environ={
|
2013-03-20 19:26:45 -07:00
|
|
|
'REQUEST_METHOD': 'PUT',
|
2010-07-12 17:03:45 -05:00
|
|
|
'HTTP_X_TIMESTAMP': '1',
|
|
|
|
'HTTP_X_CONTENT_TYPE': 'text/plain',
|
|
|
|
'HTTP_X_ETAG': 'x',
|
|
|
|
'HTTP_X_SIZE': 0})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
|
|
|
# test format
|
2013-09-01 15:10:39 -04:00
|
|
|
json_body = [{"name": "0",
|
|
|
|
"hash": "x",
|
|
|
|
"bytes": 0,
|
|
|
|
"content_type": "text/plain",
|
|
|
|
"last_modified": "1970-01-01T00:00:01.000000"},
|
|
|
|
{"name": "1",
|
|
|
|
"hash": "x",
|
|
|
|
"bytes": 0,
|
|
|
|
"content_type": "text/plain",
|
|
|
|
"last_modified": "1970-01-01T00:00:01.000000"},
|
|
|
|
{"name": "2",
|
|
|
|
"hash": "x",
|
|
|
|
"bytes": 0,
|
|
|
|
"content_type": "text/plain",
|
|
|
|
"last_modified": "1970-01-01T00:00:01.000000"}]
|
|
|
|
|
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/jsonc?format=json',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.content_type, 'application/json')
|
2013-05-28 07:22:42 -07:00
|
|
|
self.assertEquals(simplejson.loads(resp.body), json_body)
|
2011-06-10 18:36:02 +00:00
|
|
|
self.assertEquals(resp.charset, 'utf-8')
|
2010-10-29 10:28:19 +00:00
|
|
|
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/jsonc?format=json',
|
|
|
|
environ={'REQUEST_METHOD': 'HEAD'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2012-11-02 12:02:02 -07:00
|
|
|
self.assertEquals(resp.content_type, 'application/json')
|
|
|
|
|
2010-10-29 10:28:19 +00:00
|
|
|
for accept in ('application/json', 'application/json;q=1.0,*/*;q=0.9',
|
2013-09-01 15:10:39 -04:00
|
|
|
'*/*;q=0.9,application/json;q=1.0', 'application/*'):
|
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/jsonc',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2010-10-29 10:28:19 +00:00
|
|
|
req.accept = accept
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-09-01 15:10:39 -04:00
|
|
|
self.assertEquals(
|
|
|
|
simplejson.loads(resp.body), json_body,
|
2010-10-29 10:28:19 +00:00
|
|
|
'Invalid body for Accept: %s' % accept)
|
2013-09-01 15:10:39 -04:00
|
|
|
self.assertEquals(
|
|
|
|
resp.content_type, 'application/json',
|
2010-10-29 10:28:19 +00:00
|
|
|
'Invalid content_type for Accept: %s' % accept)
|
2010-10-29 01:23:01 +00:00
|
|
|
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/jsonc',
|
|
|
|
environ={'REQUEST_METHOD': 'HEAD'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
req.accept = accept
|
|
|
|
resp = req.get_response(self.controller)
|
2013-09-01 15:10:39 -04:00
|
|
|
self.assertEquals(
|
|
|
|
resp.content_type, 'application/json',
|
2012-11-02 12:02:02 -07:00
|
|
|
'Invalid content_type for Accept: %s' % accept)
|
|
|
|
|
2010-10-29 01:23:01 +00:00
|
|
|
def test_GET_plain(self):
|
|
|
|
# make a container
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/plainc', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-10-29 10:28:19 +00:00
|
|
|
# test an empty container
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/plainc', environ={'REQUEST_METHOD': 'GET'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-10-29 10:28:19 +00:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
2010-10-29 01:23:01 +00:00
|
|
|
# fill the container
|
|
|
|
for i in range(3):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/plainc/%s' % i, environ={
|
2013-03-20 19:26:45 -07:00
|
|
|
'REQUEST_METHOD': 'PUT',
|
2010-10-29 01:23:01 +00:00
|
|
|
'HTTP_X_TIMESTAMP': '1',
|
|
|
|
'HTTP_X_CONTENT_TYPE': 'text/plain',
|
|
|
|
'HTTP_X_ETAG': 'x',
|
|
|
|
'HTTP_X_SIZE': 0})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-10-29 01:23:01 +00:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
|
|
|
plain_body = '0\n1\n2\n'
|
|
|
|
|
|
|
|
req = Request.blank('/sda1/p/a/plainc',
|
2013-09-01 15:10:39 -04:00
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-10-29 01:23:01 +00:00
|
|
|
self.assertEquals(resp.content_type, 'text/plain')
|
|
|
|
self.assertEquals(resp.body, plain_body)
|
2011-06-10 18:36:02 +00:00
|
|
|
self.assertEquals(resp.charset, 'utf-8')
|
2010-10-29 01:23:01 +00:00
|
|
|
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
req = Request.blank('/sda1/p/a/plainc',
|
2013-09-01 15:10:39 -04:00
|
|
|
environ={'REQUEST_METHOD': 'HEAD'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2012-11-02 12:02:02 -07:00
|
|
|
self.assertEquals(resp.content_type, 'text/plain')
|
|
|
|
|
2010-10-29 01:23:01 +00:00
|
|
|
for accept in ('', 'text/plain', 'application/xml;q=0.8,*/*;q=0.9',
|
2013-09-01 15:10:39 -04:00
|
|
|
'*/*;q=0.9,application/xml;q=0.8', '*/*',
|
|
|
|
'text/plain,application/xml'):
|
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/plainc',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2010-10-29 01:23:01 +00:00
|
|
|
req.accept = accept
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-09-01 15:10:39 -04:00
|
|
|
self.assertEquals(
|
|
|
|
resp.body, plain_body,
|
2010-10-29 10:28:19 +00:00
|
|
|
'Invalid body for Accept: %s' % accept)
|
2013-09-01 15:10:39 -04:00
|
|
|
self.assertEquals(
|
|
|
|
resp.content_type, 'text/plain',
|
2010-10-29 10:28:19 +00:00
|
|
|
'Invalid content_type for Accept: %s' % accept)
|
2010-10-29 01:23:01 +00:00
|
|
|
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/plainc',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
req.accept = accept
|
|
|
|
resp = req.get_response(self.controller)
|
2013-09-01 15:10:39 -04:00
|
|
|
self.assertEquals(
|
|
|
|
resp.content_type, 'text/plain',
|
2012-11-02 12:02:02 -07:00
|
|
|
'Invalid content_type for Accept: %s' % accept)
|
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
# test conflicting formats
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/plainc?format=plain',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2010-07-12 17:03:45 -05:00
|
|
|
req.accept = 'application/json'
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.content_type, 'text/plain')
|
2010-10-29 01:23:01 +00:00
|
|
|
self.assertEquals(resp.body, plain_body)
|
|
|
|
|
2012-07-31 02:26:39 +00:00
|
|
|
# test unknown format uses default plain
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/plainc?format=somethingelse',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2012-07-31 02:26:39 +00:00
|
|
|
self.assertEquals(resp.status_int, 200)
|
|
|
|
self.assertEquals(resp.content_type, 'text/plain')
|
|
|
|
self.assertEquals(resp.body, plain_body)
|
|
|
|
|
2012-02-02 11:54:15 +00:00
|
|
|
def test_GET_json_last_modified(self):
|
|
|
|
# make a container
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/jsonc', environ={
|
|
|
|
'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-09-01 15:10:39 -04:00
|
|
|
for i, d in [(0, 1.5), (1, 1.0), ]:
|
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/jsonc/%s' % i, environ={
|
2013-03-20 19:26:45 -07:00
|
|
|
'REQUEST_METHOD': 'PUT',
|
2012-02-02 11:54:15 +00:00
|
|
|
'HTTP_X_TIMESTAMP': d,
|
|
|
|
'HTTP_X_CONTENT_TYPE': 'text/plain',
|
|
|
|
'HTTP_X_ETAG': 'x',
|
|
|
|
'HTTP_X_SIZE': 0})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2012-02-02 11:54:15 +00:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
|
|
|
# test format
|
|
|
|
# last_modified format must be uniform, even when there are not msecs
|
2013-09-01 15:10:39 -04:00
|
|
|
json_body = [{"name": "0",
|
|
|
|
"hash": "x",
|
|
|
|
"bytes": 0,
|
|
|
|
"content_type": "text/plain",
|
|
|
|
"last_modified": "1970-01-01T00:00:01.500000"},
|
|
|
|
{"name": "1",
|
|
|
|
"hash": "x",
|
|
|
|
"bytes": 0,
|
|
|
|
"content_type": "text/plain",
|
|
|
|
"last_modified": "1970-01-01T00:00:01.000000"}, ]
|
|
|
|
|
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/jsonc?format=json',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2012-02-02 11:54:15 +00:00
|
|
|
self.assertEquals(resp.content_type, 'application/json')
|
2013-05-28 07:22:42 -07:00
|
|
|
self.assertEquals(simplejson.loads(resp.body), json_body)
|
2012-02-02 11:54:15 +00:00
|
|
|
self.assertEquals(resp.charset, 'utf-8')
|
|
|
|
|
2010-10-29 01:23:01 +00:00
|
|
|
def test_GET_xml(self):
|
|
|
|
# make a container
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/xmlc', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-10-29 01:23:01 +00:00
|
|
|
# fill the container
|
|
|
|
for i in range(3):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/xmlc/%s' % i,
|
2013-03-20 19:26:45 -07:00
|
|
|
environ={
|
|
|
|
'REQUEST_METHOD': 'PUT',
|
2011-07-22 10:54:54 -07:00
|
|
|
'HTTP_X_TIMESTAMP': '1',
|
|
|
|
'HTTP_X_CONTENT_TYPE': 'text/plain',
|
|
|
|
'HTTP_X_ETAG': 'x',
|
|
|
|
'HTTP_X_SIZE': 0})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-10-29 01:23:01 +00:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
2013-08-30 20:09:26 +00:00
|
|
|
xml_body = '<?xml version="1.0" encoding="UTF-8"?>\n' \
|
2010-10-29 01:23:01 +00:00
|
|
|
'<container name="xmlc">' \
|
2013-09-01 15:10:39 -04:00
|
|
|
'<object><name>0</name><hash>x</hash><bytes>0</bytes>' \
|
|
|
|
'<content_type>text/plain</content_type>' \
|
|
|
|
'<last_modified>1970-01-01T00:00:01.000000' \
|
|
|
|
'</last_modified></object>' \
|
|
|
|
'<object><name>1</name><hash>x</hash><bytes>0</bytes>' \
|
|
|
|
'<content_type>text/plain</content_type>' \
|
|
|
|
'<last_modified>1970-01-01T00:00:01.000000' \
|
|
|
|
'</last_modified></object>' \
|
|
|
|
'<object><name>2</name><hash>x</hash><bytes>0</bytes>' \
|
|
|
|
'<content_type>text/plain</content_type>' \
|
|
|
|
'<last_modified>1970-01-01T00:00:01.000000' \
|
|
|
|
'</last_modified></object>' \
|
2010-10-29 01:23:01 +00:00
|
|
|
'</container>'
|
2013-07-23 14:54:51 -07:00
|
|
|
|
2010-10-29 01:23:01 +00:00
|
|
|
# tests
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/xmlc?format=xml',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-10-29 01:23:01 +00:00
|
|
|
self.assertEquals(resp.content_type, 'application/xml')
|
|
|
|
self.assertEquals(resp.body, xml_body)
|
2011-06-10 18:36:02 +00:00
|
|
|
self.assertEquals(resp.charset, 'utf-8')
|
2010-10-29 01:23:01 +00:00
|
|
|
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/xmlc?format=xml',
|
|
|
|
environ={'REQUEST_METHOD': 'HEAD'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2012-11-02 12:02:02 -07:00
|
|
|
self.assertEquals(resp.content_type, 'application/xml')
|
|
|
|
|
2013-09-01 15:10:39 -04:00
|
|
|
for xml_accept in (
|
|
|
|
'application/xml', 'application/xml;q=1.0,*/*;q=0.9',
|
|
|
|
'*/*;q=0.9,application/xml;q=1.0', 'application/xml,text/xml'):
|
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/xmlc',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2010-10-29 01:23:01 +00:00
|
|
|
req.accept = xml_accept
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-09-01 15:10:39 -04:00
|
|
|
self.assertEquals(
|
|
|
|
resp.body, xml_body,
|
2010-10-29 10:28:19 +00:00
|
|
|
'Invalid body for Accept: %s' % xml_accept)
|
2013-09-01 15:10:39 -04:00
|
|
|
self.assertEquals(
|
|
|
|
resp.content_type, 'application/xml',
|
2010-10-29 10:28:19 +00:00
|
|
|
'Invalid content_type for Accept: %s' % xml_accept)
|
2010-07-12 17:03:45 -05:00
|
|
|
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/xmlc',
|
|
|
|
environ={'REQUEST_METHOD': 'HEAD'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
req.accept = xml_accept
|
|
|
|
resp = req.get_response(self.controller)
|
2013-09-01 15:10:39 -04:00
|
|
|
self.assertEquals(
|
|
|
|
resp.content_type, 'application/xml',
|
2012-11-02 12:02:02 -07:00
|
|
|
'Invalid content_type for Accept: %s' % xml_accept)
|
|
|
|
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/xmlc',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2010-11-02 16:04:15 +00:00
|
|
|
req.accept = 'text/xml'
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-11-02 16:04:15 +00:00
|
|
|
self.assertEquals(resp.content_type, 'text/xml')
|
|
|
|
self.assertEquals(resp.body, xml_body)
|
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def test_GET_marker(self):
|
|
|
|
# make a container
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
# fill the container
|
|
|
|
for i in range(3):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c/%s' % i, environ={
|
|
|
|
'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '1',
|
|
|
|
'HTTP_X_CONTENT_TYPE': 'text/plain',
|
2010-07-12 17:03:45 -05:00
|
|
|
'HTTP_X_ETAG': 'x', 'HTTP_X_SIZE': 0})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
|
|
|
# test limit with marker
|
2013-08-16 16:24:00 -07:00
|
|
|
req = Request.blank('/sda1/p/a/c?limit=2&marker=1',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
result = resp.body.split()
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(result, ['2', ])
|
2010-07-12 17:03:45 -05:00
|
|
|
|
|
|
|
def test_weird_content_types(self):
|
|
|
|
snowman = u'\u2603'
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-07-23 14:54:51 -07:00
|
|
|
for i, ctype in enumerate((snowman.encode('utf-8'),
|
|
|
|
'text/plain; charset="utf-8"')):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c/%s' % i, environ={
|
2013-03-20 19:26:45 -07:00
|
|
|
'REQUEST_METHOD': 'PUT',
|
2010-07-12 17:03:45 -05:00
|
|
|
'HTTP_X_TIMESTAMP': '1', 'HTTP_X_CONTENT_TYPE': ctype,
|
|
|
|
'HTTP_X_ETAG': 'x', 'HTTP_X_SIZE': 0})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
2013-08-16 16:24:00 -07:00
|
|
|
req = Request.blank('/sda1/p/a/c?format=json',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
result = [x['content_type'] for x in simplejson.loads(resp.body)]
|
2013-07-23 14:54:51 -07:00
|
|
|
self.assertEquals(result, [u'\u2603', 'text/plain;charset="utf-8"'])
|
2012-11-29 13:29:00 -08:00
|
|
|
|
2011-11-16 13:48:23 -06:00
|
|
|
def test_GET_accept_not_valid(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
2013-08-16 16:24:00 -07:00
|
|
|
req.get_response(self.controller)
|
2011-11-16 13:48:23 -06:00
|
|
|
req = Request.blank('/sda1/p/a/c1', environ={'REQUEST_METHOD': 'PUT'},
|
|
|
|
headers={'X-Put-Timestamp': '1',
|
|
|
|
'X-Delete-Timestamp': '0',
|
|
|
|
'X-Object-Count': '0',
|
|
|
|
'X-Bytes-Used': '0',
|
|
|
|
'X-Timestamp': normalize_timestamp(0)})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
req.get_response(self.controller)
|
2011-11-16 13:48:23 -06:00
|
|
|
req = Request.blank('/sda1/p/a/c', environ={'REQUEST_METHOD': 'GET'})
|
|
|
|
req.accept = 'application/xml*'
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2012-11-29 13:29:00 -08:00
|
|
|
self.assertEquals(resp.status_int, 406)
|
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def test_GET_limit(self):
|
|
|
|
# make a container
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
# fill the container
|
|
|
|
for i in range(3):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c/%s' % i,
|
2013-03-20 19:26:45 -07:00
|
|
|
environ={
|
|
|
|
'REQUEST_METHOD': 'PUT',
|
2010-07-12 17:03:45 -05:00
|
|
|
'HTTP_X_TIMESTAMP': '1',
|
|
|
|
'HTTP_X_CONTENT_TYPE': 'text/plain',
|
|
|
|
'HTTP_X_ETAG': 'x',
|
|
|
|
'HTTP_X_SIZE': 0})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
|
|
|
# test limit
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c?limit=2', environ={'REQUEST_METHOD': 'GET'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
result = resp.body.split()
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(result, ['0', '1'])
|
2010-07-12 17:03:45 -05:00
|
|
|
|
|
|
|
def test_GET_prefix(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
for i in ('a1', 'b1', 'a2', 'b2', 'a3', 'b3'):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c/%s' % i,
|
2013-03-20 19:26:45 -07:00
|
|
|
environ={
|
|
|
|
'REQUEST_METHOD': 'PUT',
|
2010-07-12 17:03:45 -05:00
|
|
|
'HTTP_X_TIMESTAMP': '1',
|
|
|
|
'HTTP_X_CONTENT_TYPE': 'text/plain',
|
|
|
|
'HTTP_X_ETAG': 'x',
|
|
|
|
'HTTP_X_SIZE': 0})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c?prefix=a', environ={'REQUEST_METHOD': 'GET'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.body.split(), ['a1', 'a2', 'a3'])
|
2010-07-12 17:03:45 -05:00
|
|
|
|
2013-05-25 16:30:07 -04:00
|
|
|
def test_GET_delimiter_too_long(self):
|
|
|
|
req = Request.blank('/sda1/p/a/c?delimiter=xx',
|
|
|
|
environ={'REQUEST_METHOD': 'GET',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-05-25 16:30:07 -04:00
|
|
|
self.assertEquals(resp.status_int, 412)
|
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def test_GET_delimiter(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
for i in ('US-TX-A', 'US-TX-B', 'US-OK-A', 'US-OK-B', 'US-UT-A'):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c/%s' % i,
|
2013-03-20 19:26:45 -07:00
|
|
|
environ={
|
|
|
|
'REQUEST_METHOD': 'PUT', 'HTTP_X_TIMESTAMP': '1',
|
2010-07-12 17:03:45 -05:00
|
|
|
'HTTP_X_CONTENT_TYPE': 'text/plain', 'HTTP_X_ETAG': 'x',
|
|
|
|
'HTTP_X_SIZE': 0})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c?prefix=US-&delimiter=-&format=json',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-09-01 15:10:39 -04:00
|
|
|
self.assertEquals(
|
|
|
|
simplejson.loads(resp.body),
|
2013-03-20 19:26:45 -07:00
|
|
|
[{"subdir": "US-OK-"},
|
|
|
|
{"subdir": "US-TX-"},
|
|
|
|
{"subdir": "US-UT-"}])
|
2010-07-12 17:03:45 -05:00
|
|
|
|
|
|
|
def test_GET_delimiter_xml(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
for i in ('US-TX-A', 'US-TX-B', 'US-OK-A', 'US-OK-B', 'US-UT-A'):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c/%s' % i,
|
2013-03-20 19:26:45 -07:00
|
|
|
environ={
|
|
|
|
'REQUEST_METHOD': 'PUT', 'HTTP_X_TIMESTAMP': '1',
|
2010-07-12 17:03:45 -05:00
|
|
|
'HTTP_X_CONTENT_TYPE': 'text/plain', 'HTTP_X_ETAG': 'x',
|
|
|
|
'HTTP_X_SIZE': 0})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c?prefix=US-&delimiter=-&format=xml',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-09-01 15:10:39 -04:00
|
|
|
self.assertEquals(
|
|
|
|
resp.body, '<?xml version="1.0" encoding="UTF-8"?>'
|
|
|
|
'\n<container name="c"><subdir name="US-OK-">'
|
|
|
|
'<name>US-OK-</name></subdir>'
|
2011-05-19 06:59:47 +00:00
|
|
|
'<subdir name="US-TX-"><name>US-TX-</name></subdir>'
|
|
|
|
'<subdir name="US-UT-"><name>US-UT-</name></subdir></container>')
|
2010-07-12 17:03:45 -05:00
|
|
|
|
2013-06-14 15:29:08 +00:00
|
|
|
def test_GET_delimiter_xml_with_quotes(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c/<\'sub\' "dir">/object',
|
2013-06-14 15:29:08 +00:00
|
|
|
environ={
|
|
|
|
'REQUEST_METHOD': 'PUT', 'HTTP_X_TIMESTAMP': '1',
|
|
|
|
'HTTP_X_CONTENT_TYPE': 'text/plain', 'HTTP_X_ETAG': 'x',
|
|
|
|
'HTTP_X_SIZE': 0})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-06-14 15:29:08 +00:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c?delimiter=/&format=xml',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-07-23 14:54:51 -07:00
|
|
|
dom = minidom.parseString(resp.body)
|
|
|
|
self.assert_(len(dom.getElementsByTagName('container')) == 1)
|
|
|
|
container = dom.getElementsByTagName('container')[0]
|
|
|
|
self.assert_(len(container.getElementsByTagName('subdir')) == 1)
|
|
|
|
subdir = container.getElementsByTagName('subdir')[0]
|
|
|
|
self.assertEquals(unicode(subdir.attributes['name'].value),
|
|
|
|
u'<\'sub\' "dir">/')
|
|
|
|
self.assert_(len(subdir.getElementsByTagName('name')) == 1)
|
|
|
|
name = subdir.getElementsByTagName('name')[0]
|
|
|
|
self.assertEquals(unicode(name.childNodes[0].data),
|
|
|
|
u'<\'sub\' "dir">/')
|
2013-06-14 15:29:08 +00:00
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def test_GET_path(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'PUT',
|
|
|
|
'HTTP_X_TIMESTAMP': '0'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
for i in ('US/TX', 'US/TX/B', 'US/OK', 'US/OK/B', 'US/UT/A'):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c/%s' % i,
|
2013-03-20 19:26:45 -07:00
|
|
|
environ={
|
|
|
|
'REQUEST_METHOD': 'PUT', 'HTTP_X_TIMESTAMP': '1',
|
2010-07-12 17:03:45 -05:00
|
|
|
'HTTP_X_CONTENT_TYPE': 'text/plain', 'HTTP_X_ETAG': 'x',
|
|
|
|
'HTTP_X_SIZE': 0})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2010-07-12 17:03:45 -05:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c?path=US&format=json',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-09-01 15:10:39 -04:00
|
|
|
self.assertEquals(
|
|
|
|
simplejson.loads(resp.body),
|
|
|
|
[{"name": "US/OK", "hash": "x", "bytes": 0,
|
|
|
|
"content_type": "text/plain",
|
|
|
|
"last_modified": "1970-01-01T00:00:01.000000"},
|
|
|
|
{"name": "US/TX", "hash": "x", "bytes": 0,
|
|
|
|
"content_type": "text/plain",
|
|
|
|
"last_modified": "1970-01-01T00:00:01.000000"}])
|
2010-07-12 17:03:45 -05:00
|
|
|
|
2013-03-20 19:26:45 -07:00
|
|
|
def test_GET_insufficient_storage(self):
|
|
|
|
self.controller = container_server.ContainerController(
|
|
|
|
{'devices': self.testdir})
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda-null/p/a/c', environ={'REQUEST_METHOD': 'GET',
|
|
|
|
'HTTP_X_TIMESTAMP': '1'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-03-20 19:26:45 -07:00
|
|
|
self.assertEquals(resp.status_int, 507)
|
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def test_through_call(self):
|
|
|
|
inbuf = StringIO()
|
|
|
|
errbuf = StringIO()
|
|
|
|
outbuf = StringIO()
|
2013-03-20 19:26:45 -07:00
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def start_response(*args):
|
|
|
|
outbuf.writelines(args)
|
2013-03-20 19:26:45 -07:00
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
self.controller.__call__({'REQUEST_METHOD': 'GET',
|
|
|
|
'SCRIPT_NAME': '',
|
|
|
|
'PATH_INFO': '/sda1/p/a/c',
|
|
|
|
'SERVER_NAME': '127.0.0.1',
|
|
|
|
'SERVER_PORT': '8080',
|
|
|
|
'SERVER_PROTOCOL': 'HTTP/1.0',
|
|
|
|
'CONTENT_LENGTH': '0',
|
|
|
|
'wsgi.version': (1, 0),
|
|
|
|
'wsgi.url_scheme': 'http',
|
|
|
|
'wsgi.input': inbuf,
|
|
|
|
'wsgi.errors': errbuf,
|
|
|
|
'wsgi.multithread': False,
|
|
|
|
'wsgi.multiprocess': False,
|
|
|
|
'wsgi.run_once': False},
|
|
|
|
start_response)
|
|
|
|
self.assertEquals(errbuf.getvalue(), '')
|
|
|
|
self.assertEquals(outbuf.getvalue()[:4], '404 ')
|
|
|
|
|
|
|
|
def test_through_call_invalid_path(self):
|
|
|
|
inbuf = StringIO()
|
|
|
|
errbuf = StringIO()
|
|
|
|
outbuf = StringIO()
|
2013-03-20 19:26:45 -07:00
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def start_response(*args):
|
|
|
|
outbuf.writelines(args)
|
2013-03-20 19:26:45 -07:00
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
self.controller.__call__({'REQUEST_METHOD': 'GET',
|
|
|
|
'SCRIPT_NAME': '',
|
|
|
|
'PATH_INFO': '/bob',
|
|
|
|
'SERVER_NAME': '127.0.0.1',
|
|
|
|
'SERVER_PORT': '8080',
|
|
|
|
'SERVER_PROTOCOL': 'HTTP/1.0',
|
|
|
|
'CONTENT_LENGTH': '0',
|
|
|
|
'wsgi.version': (1, 0),
|
|
|
|
'wsgi.url_scheme': 'http',
|
|
|
|
'wsgi.input': inbuf,
|
|
|
|
'wsgi.errors': errbuf,
|
|
|
|
'wsgi.multithread': False,
|
|
|
|
'wsgi.multiprocess': False,
|
|
|
|
'wsgi.run_once': False},
|
|
|
|
start_response)
|
|
|
|
self.assertEquals(errbuf.getvalue(), '')
|
|
|
|
self.assertEquals(outbuf.getvalue()[:4], '400 ')
|
|
|
|
|
2013-03-20 19:26:45 -07:00
|
|
|
def test_through_call_invalid_path_utf8(self):
|
|
|
|
inbuf = StringIO()
|
|
|
|
errbuf = StringIO()
|
|
|
|
outbuf = StringIO()
|
|
|
|
|
|
|
|
def start_response(*args):
|
|
|
|
outbuf.writelines(args)
|
|
|
|
|
|
|
|
self.controller.__call__({'REQUEST_METHOD': 'GET',
|
|
|
|
'SCRIPT_NAME': '',
|
|
|
|
'PATH_INFO': '\x00',
|
|
|
|
'SERVER_NAME': '127.0.0.1',
|
|
|
|
'SERVER_PORT': '8080',
|
|
|
|
'SERVER_PROTOCOL': 'HTTP/1.0',
|
|
|
|
'CONTENT_LENGTH': '0',
|
|
|
|
'wsgi.version': (1, 0),
|
|
|
|
'wsgi.url_scheme': 'http',
|
|
|
|
'wsgi.input': inbuf,
|
|
|
|
'wsgi.errors': errbuf,
|
|
|
|
'wsgi.multithread': False,
|
|
|
|
'wsgi.multiprocess': False,
|
|
|
|
'wsgi.run_once': False},
|
|
|
|
start_response)
|
|
|
|
self.assertEquals(errbuf.getvalue(), '')
|
|
|
|
self.assertEquals(outbuf.getvalue()[:4], '412 ')
|
|
|
|
|
2012-06-01 16:39:35 +02:00
|
|
|
def test_invalid_method_doesnt_exist(self):
|
|
|
|
errbuf = StringIO()
|
|
|
|
outbuf = StringIO()
|
2013-03-20 19:26:45 -07:00
|
|
|
|
2012-06-01 16:39:35 +02:00
|
|
|
def start_response(*args):
|
|
|
|
outbuf.writelines(args)
|
2013-03-20 19:26:45 -07:00
|
|
|
|
2012-06-01 16:39:35 +02:00
|
|
|
self.controller.__call__({'REQUEST_METHOD': 'method_doesnt_exist',
|
|
|
|
'PATH_INFO': '/sda1/p/a/c'},
|
|
|
|
start_response)
|
|
|
|
self.assertEquals(errbuf.getvalue(), '')
|
|
|
|
self.assertEquals(outbuf.getvalue()[:4], '405 ')
|
|
|
|
|
|
|
|
def test_invalid_method_is_not_public(self):
|
|
|
|
errbuf = StringIO()
|
|
|
|
outbuf = StringIO()
|
2013-03-20 19:26:45 -07:00
|
|
|
|
2012-06-01 16:39:35 +02:00
|
|
|
def start_response(*args):
|
|
|
|
outbuf.writelines(args)
|
2013-03-20 19:26:45 -07:00
|
|
|
|
2012-06-01 16:39:35 +02:00
|
|
|
self.controller.__call__({'REQUEST_METHOD': '__init__',
|
|
|
|
'PATH_INFO': '/sda1/p/a/c'},
|
|
|
|
start_response)
|
|
|
|
self.assertEquals(errbuf.getvalue(), '')
|
|
|
|
self.assertEquals(outbuf.getvalue()[:4], '405 ')
|
|
|
|
|
2012-06-06 03:39:53 +09:00
|
|
|
def test_params_format(self):
|
2013-09-01 15:10:39 -04:00
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
|
|
|
headers={'X-Timestamp': normalize_timestamp(1)},
|
|
|
|
environ={'REQUEST_METHOD': 'PUT'})
|
2013-08-16 16:24:00 -07:00
|
|
|
req.get_response(self.controller)
|
2012-06-06 03:39:53 +09:00
|
|
|
for format in ('xml', 'json'):
|
|
|
|
req = Request.blank('/sda1/p/a/c?format=%s' % format,
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2012-06-06 03:39:53 +09:00
|
|
|
self.assertEquals(resp.status_int, 200)
|
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
def test_params_utf8(self):
|
2013-05-25 16:30:07 -04:00
|
|
|
# Bad UTF8 sequence, all parameters should cause 400 error
|
|
|
|
for param in ('delimiter', 'limit', 'marker', 'path', 'prefix',
|
|
|
|
'end_marker', 'format'):
|
2010-07-12 17:03:45 -05:00
|
|
|
req = Request.blank('/sda1/p/a/c?%s=\xce' % param,
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2013-07-24 12:55:25 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-05-25 16:30:07 -04:00
|
|
|
self.assertEquals(resp.status_int, 400,
|
|
|
|
"%d on param %s" % (resp.status_int, param))
|
|
|
|
# Good UTF8 sequence for delimiter, too long (1 byte delimiters only)
|
|
|
|
req = Request.blank('/sda1/p/a/c?delimiter=\xce\xa9',
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-05-25 16:30:07 -04:00
|
|
|
self.assertEquals(resp.status_int, 412,
|
|
|
|
"%d on param delimiter" % (resp.status_int))
|
2013-08-16 16:24:00 -07:00
|
|
|
req = Request.blank('/sda1/p/a/c',
|
2013-05-25 16:30:07 -04:00
|
|
|
headers={'X-Timestamp': normalize_timestamp(1)},
|
2013-08-16 16:24:00 -07:00
|
|
|
environ={'REQUEST_METHOD': 'PUT'})
|
|
|
|
req.get_response(self.controller)
|
2013-05-25 16:30:07 -04:00
|
|
|
# Good UTF8 sequence, ignored for limit, doesn't affect other queries
|
|
|
|
for param in ('limit', 'marker', 'path', 'prefix', 'end_marker',
|
|
|
|
'format'):
|
2010-07-12 17:03:45 -05:00
|
|
|
req = Request.blank('/sda1/p/a/c?%s=\xce\xa9' % param,
|
|
|
|
environ={'REQUEST_METHOD': 'GET'})
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2013-05-25 16:30:07 -04:00
|
|
|
self.assertEquals(resp.status_int, 204,
|
|
|
|
"%d on param %s" % (resp.status_int, param))
|
2010-07-12 17:03:45 -05:00
|
|
|
|
2011-10-26 21:42:24 +00:00
|
|
|
def test_put_auto_create(self):
|
|
|
|
headers = {'x-timestamp': normalize_timestamp(1),
|
|
|
|
'x-size': '0',
|
|
|
|
'x-content-type': 'text/plain',
|
|
|
|
'x-etag': 'd41d8cd98f00b204e9800998ecf8427e'}
|
|
|
|
|
2013-08-16 16:24:00 -07:00
|
|
|
req = Request.blank('/sda1/p/a/c/o',
|
|
|
|
environ={'REQUEST_METHOD': 'PUT'},
|
|
|
|
headers=dict(headers))
|
|
|
|
resp = req.get_response(self.controller)
|
2011-10-26 21:42:24 +00:00
|
|
|
self.assertEquals(resp.status_int, 404)
|
|
|
|
|
2013-08-16 16:24:00 -07:00
|
|
|
req = Request.blank('/sda1/p/.a/c/o',
|
2013-09-01 15:10:39 -04:00
|
|
|
environ={'REQUEST_METHOD': 'PUT'},
|
|
|
|
headers=dict(headers))
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2011-10-26 21:42:24 +00:00
|
|
|
self.assertEquals(resp.status_int, 201)
|
|
|
|
|
2013-08-16 16:24:00 -07:00
|
|
|
req = Request.blank('/sda1/p/a/.c/o',
|
2013-09-01 15:10:39 -04:00
|
|
|
environ={'REQUEST_METHOD': 'PUT'},
|
|
|
|
headers=dict(headers))
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2011-10-26 21:42:24 +00:00
|
|
|
self.assertEquals(resp.status_int, 404)
|
|
|
|
|
2013-08-16 16:24:00 -07:00
|
|
|
req = Request.blank('/sda1/p/a/c/.o',
|
2013-09-01 15:10:39 -04:00
|
|
|
environ={'REQUEST_METHOD': 'PUT'},
|
|
|
|
headers=dict(headers))
|
2013-08-16 16:24:00 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2011-10-26 21:42:24 +00:00
|
|
|
self.assertEquals(resp.status_int, 404)
|
|
|
|
|
|
|
|
def test_delete_auto_create(self):
|
|
|
|
headers = {'x-timestamp': normalize_timestamp(1)}
|
|
|
|
|
2013-08-16 16:24:00 -07:00
|
|
|
req = Request.blank('/sda1/p/a/c/o',
|
|
|
|
environ={'REQUEST_METHOD': 'DELETE'},
|
|
|
|
headers=dict(headers))
|
|
|
|
resp = req.get_response(self.controller)
|
2011-10-26 21:42:24 +00:00
|
|
|
self.assertEquals(resp.status_int, 404)
|
|
|
|
|
2013-08-16 16:24:00 -07:00
|
|
|
req = Request.blank('/sda1/p/.a/c/o',
|
|
|
|
environ={'REQUEST_METHOD': 'DELETE'},
|
|
|
|
headers=dict(headers))
|
|
|
|
resp = req.get_response(self.controller)
|
2011-10-26 21:42:24 +00:00
|
|
|
self.assertEquals(resp.status_int, 204)
|
|
|
|
|
2013-08-16 16:24:00 -07:00
|
|
|
req = Request.blank('/sda1/p/a/.c/o',
|
|
|
|
environ={'REQUEST_METHOD': 'DELETE'},
|
|
|
|
headers=dict(headers))
|
|
|
|
resp = req.get_response(self.controller)
|
2011-10-26 21:42:24 +00:00
|
|
|
self.assertEquals(resp.status_int, 404)
|
|
|
|
|
2013-08-16 16:24:00 -07:00
|
|
|
req = Request.blank('/sda1/p/a/.c/.o',
|
|
|
|
environ={'REQUEST_METHOD': 'DELETE'},
|
|
|
|
headers=dict(headers))
|
|
|
|
resp = req.get_response(self.controller)
|
2011-10-26 21:42:24 +00:00
|
|
|
self.assertEquals(resp.status_int, 404)
|
|
|
|
|
2012-11-02 12:02:02 -07:00
|
|
|
def test_content_type_on_HEAD(self):
|
2013-08-16 16:24:00 -07:00
|
|
|
Request.blank('/sda1/p/a/o',
|
|
|
|
headers={'X-Timestamp': normalize_timestamp(1)},
|
|
|
|
environ={'REQUEST_METHOD': 'PUT'}).get_response(
|
|
|
|
self.controller)
|
2012-11-02 12:02:02 -07:00
|
|
|
|
|
|
|
env = {'REQUEST_METHOD': 'HEAD'}
|
|
|
|
|
|
|
|
req = Request.blank('/sda1/p/a/o?format=xml', environ=env)
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2012-11-02 12:02:02 -07:00
|
|
|
self.assertEquals(resp.content_type, 'application/xml')
|
|
|
|
self.assertEquals(resp.charset, 'utf-8')
|
|
|
|
|
|
|
|
req = Request.blank('/sda1/p/a/o?format=json', environ=env)
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2012-11-02 12:02:02 -07:00
|
|
|
self.assertEquals(resp.content_type, 'application/json')
|
|
|
|
self.assertEquals(resp.charset, 'utf-8')
|
|
|
|
|
|
|
|
req = Request.blank('/sda1/p/a/o', environ=env)
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2012-11-02 12:02:02 -07:00
|
|
|
self.assertEquals(resp.content_type, 'text/plain')
|
|
|
|
self.assertEquals(resp.charset, 'utf-8')
|
|
|
|
|
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/o', headers={'Accept': 'application/json'}, environ=env)
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2012-11-02 12:02:02 -07:00
|
|
|
self.assertEquals(resp.content_type, 'application/json')
|
|
|
|
self.assertEquals(resp.charset, 'utf-8')
|
|
|
|
|
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/o', headers={'Accept': 'application/xml'}, environ=env)
|
Refactor how we pick listings' content type.
There were a few different places where we had some repeated code to
figure out what format an account or container listing response should
be in (text, JSON, or XML). Now that's been pulled into a single
function.
As part of this, you can now raise HTTPException subclasses in proxy
controllers instead of laboriously plumbing error responses all the
way back up to swift.proxy.server.Application.handle_request(). This
lets us avoid certain ugly patterns, like the one where a method
returns a tuple of (x, y, z, error) and the caller has to see if it
got (value, value, value, None) or (None, None, None, errorvalue). Now
we can just raise the error.
Change-Id: I316873df289160d526487ad116f6fbb9a575e3de
2013-08-14 11:55:15 -07:00
|
|
|
resp = req.get_response(self.controller)
|
2012-11-02 12:02:02 -07:00
|
|
|
self.assertEquals(resp.content_type, 'application/xml')
|
|
|
|
self.assertEquals(resp.charset, 'utf-8')
|
|
|
|
|
Allow for multiple X-(Account|Container)-* headers.
When the number of account/container or container/object replicas are
different, Swift had a few misbehaviors. This commit fixes them.
* On an object PUT/POST/DELETE, if there were 3 object replicas and
only 2 container replicas, then only 2 requests would be made to
object servers. Now, 3 requests will be made, but the third won't
have any X-Container-* headers in it.
* On an object PUT/POST/DELETE, if there were 3 object replicas and 4
container replicas, then only 3/4 container servers would receive
immediate updates; the fourth would be ignored. Now one of the
object servers will receive multiple (comma-separated) values in the
X-Container-* headers and it will attempt to contact both of them.
One side effect is that multiple async_pendings may be written for
updates to the same object. They'll have differing timestamps,
though, so all but the newest will be deleted unread. To trigger
this behavior, you have to have more container replicas than object
replicas, 2 or more of the container servers must be down, and the
headers sent to one object server must reference 2 or more down
container servers; it's unlikely enough and the consequences are so
minor that it didn't seem worth fixing.
The situation with account/containers is analogous, only without the
async_pendings.
Change-Id: I98bc2de93fb6b2346d6de1d764213d7563653e8d
2012-12-12 17:47:04 -08:00
|
|
|
def test_updating_multiple_container_servers(self):
|
|
|
|
http_connect_args = []
|
2013-03-20 19:26:45 -07:00
|
|
|
|
Allow for multiple X-(Account|Container)-* headers.
When the number of account/container or container/object replicas are
different, Swift had a few misbehaviors. This commit fixes them.
* On an object PUT/POST/DELETE, if there were 3 object replicas and
only 2 container replicas, then only 2 requests would be made to
object servers. Now, 3 requests will be made, but the third won't
have any X-Container-* headers in it.
* On an object PUT/POST/DELETE, if there were 3 object replicas and 4
container replicas, then only 3/4 container servers would receive
immediate updates; the fourth would be ignored. Now one of the
object servers will receive multiple (comma-separated) values in the
X-Container-* headers and it will attempt to contact both of them.
One side effect is that multiple async_pendings may be written for
updates to the same object. They'll have differing timestamps,
though, so all but the newest will be deleted unread. To trigger
this behavior, you have to have more container replicas than object
replicas, 2 or more of the container servers must be down, and the
headers sent to one object server must reference 2 or more down
container servers; it's unlikely enough and the consequences are so
minor that it didn't seem worth fixing.
The situation with account/containers is analogous, only without the
async_pendings.
Change-Id: I98bc2de93fb6b2346d6de1d764213d7563653e8d
2012-12-12 17:47:04 -08:00
|
|
|
def fake_http_connect(ipaddr, port, device, partition, method, path,
|
|
|
|
headers=None, query_string=None, ssl=False):
|
2013-03-20 19:26:45 -07:00
|
|
|
|
Allow for multiple X-(Account|Container)-* headers.
When the number of account/container or container/object replicas are
different, Swift had a few misbehaviors. This commit fixes them.
* On an object PUT/POST/DELETE, if there were 3 object replicas and
only 2 container replicas, then only 2 requests would be made to
object servers. Now, 3 requests will be made, but the third won't
have any X-Container-* headers in it.
* On an object PUT/POST/DELETE, if there were 3 object replicas and 4
container replicas, then only 3/4 container servers would receive
immediate updates; the fourth would be ignored. Now one of the
object servers will receive multiple (comma-separated) values in the
X-Container-* headers and it will attempt to contact both of them.
One side effect is that multiple async_pendings may be written for
updates to the same object. They'll have differing timestamps,
though, so all but the newest will be deleted unread. To trigger
this behavior, you have to have more container replicas than object
replicas, 2 or more of the container servers must be down, and the
headers sent to one object server must reference 2 or more down
container servers; it's unlikely enough and the consequences are so
minor that it didn't seem worth fixing.
The situation with account/containers is analogous, only without the
async_pendings.
Change-Id: I98bc2de93fb6b2346d6de1d764213d7563653e8d
2012-12-12 17:47:04 -08:00
|
|
|
class SuccessfulFakeConn(object):
|
|
|
|
@property
|
|
|
|
def status(self):
|
|
|
|
return 200
|
|
|
|
|
|
|
|
def getresponse(self):
|
|
|
|
return self
|
|
|
|
|
|
|
|
def read(self):
|
|
|
|
return ''
|
|
|
|
|
|
|
|
captured_args = {'ipaddr': ipaddr, 'port': port,
|
|
|
|
'device': device, 'partition': partition,
|
|
|
|
'method': method, 'path': path, 'ssl': ssl,
|
|
|
|
'headers': headers, 'query_string': query_string}
|
|
|
|
|
|
|
|
http_connect_args.append(
|
2013-03-20 19:26:45 -07:00
|
|
|
dict((k, v) for k, v in captured_args.iteritems()
|
Allow for multiple X-(Account|Container)-* headers.
When the number of account/container or container/object replicas are
different, Swift had a few misbehaviors. This commit fixes them.
* On an object PUT/POST/DELETE, if there were 3 object replicas and
only 2 container replicas, then only 2 requests would be made to
object servers. Now, 3 requests will be made, but the third won't
have any X-Container-* headers in it.
* On an object PUT/POST/DELETE, if there were 3 object replicas and 4
container replicas, then only 3/4 container servers would receive
immediate updates; the fourth would be ignored. Now one of the
object servers will receive multiple (comma-separated) values in the
X-Container-* headers and it will attempt to contact both of them.
One side effect is that multiple async_pendings may be written for
updates to the same object. They'll have differing timestamps,
though, so all but the newest will be deleted unread. To trigger
this behavior, you have to have more container replicas than object
replicas, 2 or more of the container servers must be down, and the
headers sent to one object server must reference 2 or more down
container servers; it's unlikely enough and the consequences are so
minor that it didn't seem worth fixing.
The situation with account/containers is analogous, only without the
async_pendings.
Change-Id: I98bc2de93fb6b2346d6de1d764213d7563653e8d
2012-12-12 17:47:04 -08:00
|
|
|
if v is not None))
|
|
|
|
|
|
|
|
req = Request.blank(
|
|
|
|
'/sda1/p/a/c',
|
|
|
|
environ={'REQUEST_METHOD': 'PUT'},
|
|
|
|
headers={'X-Timestamp': '12345',
|
|
|
|
'X-Account-Partition': '30',
|
|
|
|
'X-Account-Host': '1.2.3.4:5, 6.7.8.9:10',
|
|
|
|
'X-Account-Device': 'sdb1, sdf1'})
|
|
|
|
|
|
|
|
orig_http_connect = container_server.http_connect
|
|
|
|
try:
|
|
|
|
container_server.http_connect = fake_http_connect
|
2013-08-16 16:24:00 -07:00
|
|
|
req.get_response(self.controller)
|
Allow for multiple X-(Account|Container)-* headers.
When the number of account/container or container/object replicas are
different, Swift had a few misbehaviors. This commit fixes them.
* On an object PUT/POST/DELETE, if there were 3 object replicas and
only 2 container replicas, then only 2 requests would be made to
object servers. Now, 3 requests will be made, but the third won't
have any X-Container-* headers in it.
* On an object PUT/POST/DELETE, if there were 3 object replicas and 4
container replicas, then only 3/4 container servers would receive
immediate updates; the fourth would be ignored. Now one of the
object servers will receive multiple (comma-separated) values in the
X-Container-* headers and it will attempt to contact both of them.
One side effect is that multiple async_pendings may be written for
updates to the same object. They'll have differing timestamps,
though, so all but the newest will be deleted unread. To trigger
this behavior, you have to have more container replicas than object
replicas, 2 or more of the container servers must be down, and the
headers sent to one object server must reference 2 or more down
container servers; it's unlikely enough and the consequences are so
minor that it didn't seem worth fixing.
The situation with account/containers is analogous, only without the
async_pendings.
Change-Id: I98bc2de93fb6b2346d6de1d764213d7563653e8d
2012-12-12 17:47:04 -08:00
|
|
|
finally:
|
|
|
|
container_server.http_connect = orig_http_connect
|
|
|
|
|
|
|
|
http_connect_args.sort(key=operator.itemgetter('ipaddr'))
|
|
|
|
|
|
|
|
self.assertEquals(len(http_connect_args), 2)
|
|
|
|
self.assertEquals(
|
|
|
|
http_connect_args[0],
|
|
|
|
{'ipaddr': '1.2.3.4',
|
|
|
|
'port': '5',
|
|
|
|
'path': '/a/c',
|
|
|
|
'device': 'sdb1',
|
|
|
|
'partition': '30',
|
|
|
|
'method': 'PUT',
|
|
|
|
'ssl': False,
|
2013-09-01 15:10:39 -04:00
|
|
|
'headers': HeaderKeyDict({
|
|
|
|
'x-bytes-used': 0,
|
|
|
|
'x-delete-timestamp': '0',
|
|
|
|
'x-object-count': 0,
|
|
|
|
'x-put-timestamp': '0000012345.00000',
|
|
|
|
'referer': 'PUT http://localhost/sda1/p/a/c',
|
|
|
|
'user-agent': 'container-server %d' % os.getpid(),
|
|
|
|
'x-trans-id': '-'})})
|
Allow for multiple X-(Account|Container)-* headers.
When the number of account/container or container/object replicas are
different, Swift had a few misbehaviors. This commit fixes them.
* On an object PUT/POST/DELETE, if there were 3 object replicas and
only 2 container replicas, then only 2 requests would be made to
object servers. Now, 3 requests will be made, but the third won't
have any X-Container-* headers in it.
* On an object PUT/POST/DELETE, if there were 3 object replicas and 4
container replicas, then only 3/4 container servers would receive
immediate updates; the fourth would be ignored. Now one of the
object servers will receive multiple (comma-separated) values in the
X-Container-* headers and it will attempt to contact both of them.
One side effect is that multiple async_pendings may be written for
updates to the same object. They'll have differing timestamps,
though, so all but the newest will be deleted unread. To trigger
this behavior, you have to have more container replicas than object
replicas, 2 or more of the container servers must be down, and the
headers sent to one object server must reference 2 or more down
container servers; it's unlikely enough and the consequences are so
minor that it didn't seem worth fixing.
The situation with account/containers is analogous, only without the
async_pendings.
Change-Id: I98bc2de93fb6b2346d6de1d764213d7563653e8d
2012-12-12 17:47:04 -08:00
|
|
|
self.assertEquals(
|
|
|
|
http_connect_args[1],
|
|
|
|
{'ipaddr': '6.7.8.9',
|
|
|
|
'port': '10',
|
|
|
|
'path': '/a/c',
|
|
|
|
'device': 'sdf1',
|
|
|
|
'partition': '30',
|
|
|
|
'method': 'PUT',
|
|
|
|
'ssl': False,
|
2013-09-01 15:10:39 -04:00
|
|
|
'headers': HeaderKeyDict({
|
|
|
|
'x-bytes-used': 0,
|
|
|
|
'x-delete-timestamp': '0',
|
|
|
|
'x-object-count': 0,
|
|
|
|
'x-put-timestamp': '0000012345.00000',
|
|
|
|
'referer': 'PUT http://localhost/sda1/p/a/c',
|
|
|
|
'user-agent': 'container-server %d' % os.getpid(),
|
|
|
|
'x-trans-id': '-'})})
|
Allow for multiple X-(Account|Container)-* headers.
When the number of account/container or container/object replicas are
different, Swift had a few misbehaviors. This commit fixes them.
* On an object PUT/POST/DELETE, if there were 3 object replicas and
only 2 container replicas, then only 2 requests would be made to
object servers. Now, 3 requests will be made, but the third won't
have any X-Container-* headers in it.
* On an object PUT/POST/DELETE, if there were 3 object replicas and 4
container replicas, then only 3/4 container servers would receive
immediate updates; the fourth would be ignored. Now one of the
object servers will receive multiple (comma-separated) values in the
X-Container-* headers and it will attempt to contact both of them.
One side effect is that multiple async_pendings may be written for
updates to the same object. They'll have differing timestamps,
though, so all but the newest will be deleted unread. To trigger
this behavior, you have to have more container replicas than object
replicas, 2 or more of the container servers must be down, and the
headers sent to one object server must reference 2 or more down
container servers; it's unlikely enough and the consequences are so
minor that it didn't seem worth fixing.
The situation with account/containers is analogous, only without the
async_pendings.
Change-Id: I98bc2de93fb6b2346d6de1d764213d7563653e8d
2012-12-12 17:47:04 -08:00
|
|
|
|
2012-12-17 06:39:25 -05:00
|
|
|
def test_serv_reserv(self):
|
2013-08-31 22:36:58 -04:00
|
|
|
# Test replication_server flag was set from configuration file.
|
2012-12-17 06:39:25 -05:00
|
|
|
container_controller = container_server.ContainerController
|
|
|
|
conf = {'devices': self.testdir, 'mount_check': 'false'}
|
|
|
|
self.assertEquals(container_controller(conf).replication_server, None)
|
|
|
|
for val in [True, '1', 'True', 'true']:
|
|
|
|
conf['replication_server'] = val
|
|
|
|
self.assertTrue(container_controller(conf).replication_server)
|
|
|
|
for val in [False, 0, '0', 'False', 'false', 'test_string']:
|
|
|
|
conf['replication_server'] = val
|
|
|
|
self.assertFalse(container_controller(conf).replication_server)
|
|
|
|
|
|
|
|
def test_list_allowed_methods(self):
|
2013-08-31 22:36:58 -04:00
|
|
|
# Test list of allowed_methods
|
2013-05-23 20:16:21 +04:00
|
|
|
obj_methods = ['DELETE', 'PUT', 'HEAD', 'GET', 'POST']
|
|
|
|
repl_methods = ['REPLICATE']
|
|
|
|
for method_name in obj_methods:
|
|
|
|
method = getattr(self.controller, method_name)
|
|
|
|
self.assertFalse(hasattr(method, 'replication'))
|
|
|
|
for method_name in repl_methods:
|
|
|
|
method = getattr(self.controller, method_name)
|
|
|
|
self.assertEquals(method.replication, True)
|
2012-12-17 06:39:25 -05:00
|
|
|
|
|
|
|
def test_correct_allowed_method(self):
|
2013-08-31 22:36:58 -04:00
|
|
|
# Test correct work for allowed method using
|
|
|
|
# swift.container.server.ContainerController.__call__
|
2012-12-17 06:39:25 -05:00
|
|
|
inbuf = StringIO()
|
|
|
|
errbuf = StringIO()
|
|
|
|
outbuf = StringIO()
|
2013-05-23 20:16:21 +04:00
|
|
|
self.controller = container_server.ContainerController(
|
|
|
|
{'devices': self.testdir, 'mount_check': 'false',
|
|
|
|
'replication_server': 'false'})
|
2012-12-17 06:39:25 -05:00
|
|
|
|
|
|
|
def start_response(*args):
|
2013-08-31 22:36:58 -04:00
|
|
|
"""Sends args to outbuf"""
|
2012-12-17 06:39:25 -05:00
|
|
|
outbuf.writelines(args)
|
|
|
|
|
2013-05-23 20:16:21 +04:00
|
|
|
method = 'PUT'
|
2012-12-17 06:39:25 -05:00
|
|
|
|
|
|
|
env = {'REQUEST_METHOD': method,
|
|
|
|
'SCRIPT_NAME': '',
|
|
|
|
'PATH_INFO': '/sda1/p/a/c',
|
|
|
|
'SERVER_NAME': '127.0.0.1',
|
|
|
|
'SERVER_PORT': '8080',
|
|
|
|
'SERVER_PROTOCOL': 'HTTP/1.0',
|
|
|
|
'CONTENT_LENGTH': '0',
|
|
|
|
'wsgi.version': (1, 0),
|
|
|
|
'wsgi.url_scheme': 'http',
|
|
|
|
'wsgi.input': inbuf,
|
|
|
|
'wsgi.errors': errbuf,
|
|
|
|
'wsgi.multithread': False,
|
|
|
|
'wsgi.multiprocess': False,
|
|
|
|
'wsgi.run_once': False}
|
|
|
|
|
2013-05-23 20:16:21 +04:00
|
|
|
method_res = mock.MagicMock()
|
|
|
|
mock_method = public(lambda x: mock.MagicMock(return_value=method_res))
|
|
|
|
with mock.patch.object(self.controller, method, new=mock_method):
|
2012-12-17 06:39:25 -05:00
|
|
|
response = self.controller.__call__(env, start_response)
|
2013-05-23 20:16:21 +04:00
|
|
|
self.assertEqual(response, method_res)
|
2012-12-17 06:39:25 -05:00
|
|
|
|
|
|
|
def test_not_allowed_method(self):
|
2013-08-31 22:36:58 -04:00
|
|
|
# Test correct work for NOT allowed method using
|
|
|
|
# swift.container.server.ContainerController.__call__
|
2012-12-17 06:39:25 -05:00
|
|
|
inbuf = StringIO()
|
|
|
|
errbuf = StringIO()
|
|
|
|
outbuf = StringIO()
|
2013-05-23 20:16:21 +04:00
|
|
|
self.controller = container_server.ContainerController(
|
|
|
|
{'devices': self.testdir, 'mount_check': 'false',
|
|
|
|
'replication_server': 'false'})
|
2012-12-17 06:39:25 -05:00
|
|
|
|
|
|
|
def start_response(*args):
|
2013-08-31 22:36:58 -04:00
|
|
|
"""Sends args to outbuf"""
|
2012-12-17 06:39:25 -05:00
|
|
|
outbuf.writelines(args)
|
|
|
|
|
2013-05-23 20:16:21 +04:00
|
|
|
method = 'PUT'
|
2012-12-17 06:39:25 -05:00
|
|
|
|
|
|
|
env = {'REQUEST_METHOD': method,
|
|
|
|
'SCRIPT_NAME': '',
|
|
|
|
'PATH_INFO': '/sda1/p/a/c',
|
|
|
|
'SERVER_NAME': '127.0.0.1',
|
|
|
|
'SERVER_PORT': '8080',
|
|
|
|
'SERVER_PROTOCOL': 'HTTP/1.0',
|
|
|
|
'CONTENT_LENGTH': '0',
|
|
|
|
'wsgi.version': (1, 0),
|
|
|
|
'wsgi.url_scheme': 'http',
|
|
|
|
'wsgi.input': inbuf,
|
|
|
|
'wsgi.errors': errbuf,
|
|
|
|
'wsgi.multithread': False,
|
|
|
|
'wsgi.multiprocess': False,
|
|
|
|
'wsgi.run_once': False}
|
|
|
|
|
|
|
|
answer = ['<html><h1>Method Not Allowed</h1><p>The method is not '
|
|
|
|
'allowed for this resource.</p></html>']
|
2013-05-23 20:16:21 +04:00
|
|
|
mock_method = replication(public(lambda x: mock.MagicMock()))
|
|
|
|
with mock.patch.object(self.controller, method, new=mock_method):
|
2012-12-17 06:39:25 -05:00
|
|
|
response = self.controller.__call__(env, start_response)
|
|
|
|
self.assertEqual(response, answer)
|
|
|
|
|
2010-07-12 17:03:45 -05:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
unittest.main()
|