Sync s3api feature branch with master
Change-Id: I9c381a0381840739e1ed3adf1c428b0e610ac768
This commit is contained in:
commit
bb064f0b30
|
@ -1,31 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# 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.
|
||||
|
||||
from __future__ import print_function
|
||||
from gettext import gettext as _
|
||||
from sys import argv, exit, stderr
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
argv[0:1] = ['swift', 'tempurl']
|
||||
print("", file=stderr)
|
||||
print(_("NOTE: This command is deprecated and will be removed "
|
||||
"in the future. Please use 'swift tempurl' instead."), file=stderr)
|
||||
print("", file=stderr)
|
||||
try:
|
||||
from swiftclient.shell import main
|
||||
except ImportError:
|
||||
print(_("ERROR: python-swiftclient not installed."), file=stderr)
|
||||
exit(1)
|
||||
exit(main(argv))
|
|
@ -1,56 +0,0 @@
|
|||
.\"
|
||||
.\" Copyright (c) 2016 OpenStack Foundation.
|
||||
.\"
|
||||
.\" Licensed under the Apache License, Version 2.0 (the "License");
|
||||
.\" you may not use this file except in compliance with the License.
|
||||
.\" You may obtain a copy of the License at
|
||||
.\"
|
||||
.\" http://www.apache.org/licenses/LICENSE-2.0
|
||||
.\"
|
||||
.\" Unless required by applicable law or agreed to in writing, software
|
||||
.\" distributed under the License is distributed on an "AS IS" BASIS,
|
||||
.\" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
.\" implied.
|
||||
.\" See the License for the specific language governing permissions and
|
||||
.\" limitations under the License.
|
||||
.\"
|
||||
.TH SWIFT-TEMP-URL "1" "August 2016" "OpenStack Swift"
|
||||
|
||||
.SH NAME
|
||||
swift\-temp\-url \- generates the query parameters for OpenStack Swift Temporary URL middleware
|
||||
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
Tool that generates the query parameters which can be used to access Swift
|
||||
objects directly from a browser by using the Temporary URL middleware.
|
||||
|
||||
.B NOTE: This command is deprecated and will be removed
|
||||
.B in the future. Please use 'swift tempurl' instead.
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B swift\-temp\-url
|
||||
\fImethod\fR \fIseconds\fR \fIpath\fR \fIkey\fR
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.I method
|
||||
The method to allow; GET for example.
|
||||
.TP
|
||||
.I seconds
|
||||
The number of seconds from now to allow requests.
|
||||
.TP
|
||||
.I path
|
||||
The full path to the resource.
|
||||
Example: \fI/v1/AUTH_account/c/o\fP
|
||||
.TP
|
||||
.I key
|
||||
The X\-Account\-Meta\-Temp\-URL\-Key for the account.
|
||||
|
||||
.SH DOCUMENTATION
|
||||
.LP
|
||||
More in depth documentation in regards to
|
||||
.BI swift\-temp\-url
|
||||
and also about OpenStack Swift as a whole can be found at
|
||||
.BI https://docs.openstack.org/swift/latest/
|
||||
and
|
||||
.BI https://docs.openstack.org
|
|
@ -131,6 +131,12 @@ Element Description
|
|||
does not require a token. In addition,
|
||||
``.r:*`` does not grant access to the
|
||||
container listing.
|
||||
``<role_name>`` A user with the specified role *name* on the
|
||||
project within which the container is stored is
|
||||
granted access. A user token scoped to the
|
||||
project must be included in the request. Access
|
||||
to the container is also granted when used in
|
||||
``X-Container-Read``.
|
||||
============================== ================================================
|
||||
|
||||
.. note::
|
||||
|
@ -211,6 +217,18 @@ project must be included in the request::
|
|||
--write-acl "77b8f82565f14814bece56e50c4c240f:*"
|
||||
|
||||
|
||||
Example: Sharing a Container with Users having a specified Role
|
||||
---------------------------------------------------------------
|
||||
|
||||
The following allows any user that has been assigned the
|
||||
``my_read_access_role`` on the project within which the ``www`` container is
|
||||
stored to download objects or to list the contents of the ``www`` container. A
|
||||
user token scoped to the project must be included in the download or list
|
||||
request::
|
||||
|
||||
swift post www --read-acl "my_read_access_role"
|
||||
|
||||
|
||||
Example: Allowing a Referrer Domain to Download Objects
|
||||
-------------------------------------------------------
|
||||
|
||||
|
|
|
@ -61,7 +61,6 @@ scripts =
|
|||
bin/swift-recon-cron
|
||||
bin/swift-ring-builder
|
||||
bin/swift-ring-builder-analyzer
|
||||
bin/swift-temp-url
|
||||
|
||||
[extras]
|
||||
kms_keymaster =
|
||||
|
|
|
@ -21,6 +21,7 @@ import signal
|
|||
from re import sub
|
||||
|
||||
import eventlet.debug
|
||||
from eventlet.hubs import use_hub
|
||||
|
||||
from swift.common import utils
|
||||
|
||||
|
@ -266,6 +267,8 @@ def run_daemon(klass, conf_file, section_name='', once=False, **kwargs):
|
|||
# and results in an exit code of 1.
|
||||
sys.exit(e)
|
||||
|
||||
use_hub(utils.get_hub())
|
||||
|
||||
# once on command line (i.e. daemonize=false) will over-ride config
|
||||
once = once or not utils.config_true_value(conf.get('daemonize', 'true'))
|
||||
|
||||
|
|
|
@ -333,7 +333,9 @@ class DatabaseBroker(object):
|
|||
exc_hint = 'malformed'
|
||||
elif 'malformed database schema' in str(exc_value):
|
||||
exc_hint = 'malformed'
|
||||
elif 'file is encrypted or is not a database' in str(exc_value):
|
||||
elif ' is not a database' in str(exc_value):
|
||||
# older versions said 'file is not a database'
|
||||
# now 'file is encrypted or is not a database'
|
||||
exc_hint = 'corrupted'
|
||||
elif 'disk I/O error' in str(exc_value):
|
||||
exc_hint = 'disk error while accessing'
|
||||
|
|
|
@ -675,7 +675,11 @@ class Bulk(object):
|
|||
'tar.bz2': 'bz2'}.get(extract_type.lower().strip('.'))
|
||||
if archive_type is not None:
|
||||
resp = HTTPOk(request=req)
|
||||
out_content_type = req.accept.best_match(ACCEPTABLE_FORMATS)
|
||||
try:
|
||||
out_content_type = req.accept.best_match(
|
||||
ACCEPTABLE_FORMATS)
|
||||
except ValueError:
|
||||
out_content_type = None # Ignore invalid header
|
||||
if out_content_type:
|
||||
resp.content_type = out_content_type
|
||||
resp.app_iter = self.handle_extract_iter(
|
||||
|
@ -684,7 +688,10 @@ class Bulk(object):
|
|||
resp = HTTPBadRequest("Unsupported archive format")
|
||||
if 'bulk-delete' in req.params and req.method in ['POST', 'DELETE']:
|
||||
resp = HTTPOk(request=req)
|
||||
out_content_type = req.accept.best_match(ACCEPTABLE_FORMATS)
|
||||
try:
|
||||
out_content_type = req.accept.best_match(ACCEPTABLE_FORMATS)
|
||||
except ValueError:
|
||||
out_content_type = None # Ignore invalid header
|
||||
if out_content_type:
|
||||
resp.content_type = out_content_type
|
||||
resp.app_iter = self.handle_delete_iter(
|
||||
|
|
|
@ -21,7 +21,7 @@ from swift.common.constraints import valid_api_version
|
|||
from swift.common.http import HTTP_NO_CONTENT
|
||||
from swift.common.request_helpers import get_param
|
||||
from swift.common.swob import HTTPException, HTTPNotAcceptable, Request, \
|
||||
RESPONSE_REASONS
|
||||
RESPONSE_REASONS, HTTPBadRequest
|
||||
|
||||
|
||||
#: Mapping of query string ``format=`` values to their corresponding
|
||||
|
@ -51,8 +51,11 @@ def get_listing_content_type(req):
|
|||
if query_format:
|
||||
req.accept = FORMAT2CONTENT_TYPE.get(
|
||||
query_format.lower(), FORMAT2CONTENT_TYPE['plain'])
|
||||
out_content_type = req.accept.best_match(
|
||||
['text/plain', 'application/json', 'application/xml', 'text/xml'])
|
||||
try:
|
||||
out_content_type = req.accept.best_match(
|
||||
['text/plain', 'application/json', 'application/xml', 'text/xml'])
|
||||
except ValueError:
|
||||
raise HTTPBadRequest(request=req, body='Invalid Accept header')
|
||||
if not out_content_type:
|
||||
raise HTTPNotAcceptable(request=req)
|
||||
return out_content_type
|
||||
|
|
|
@ -366,7 +366,7 @@ def parse_and_validate_input(req_body, req_path):
|
|||
except (TypeError, ValueError):
|
||||
errors.append("Index %d: invalid size_bytes" % seg_index)
|
||||
continue
|
||||
if seg_size < 1:
|
||||
if seg_size < 1 and seg_index != (len(parsed_data) - 1):
|
||||
errors.append("Index %d: too small; each segment must be "
|
||||
"at least 1 byte."
|
||||
% (seg_index,))
|
||||
|
@ -430,7 +430,7 @@ class SloGetContext(WSGIContext):
|
|||
if not sub_resp.is_success:
|
||||
close_if_possible(sub_resp.app_iter)
|
||||
raise ListingIterError(
|
||||
'ERROR: while fetching %s, GET of submanifest %s '
|
||||
'while fetching %s, GET of submanifest %s '
|
||||
'failed with status %d' % (req.path, sub_req.path,
|
||||
sub_resp.status_int))
|
||||
|
||||
|
@ -439,7 +439,7 @@ class SloGetContext(WSGIContext):
|
|||
return json.loads(''.join(sub_resp.app_iter))
|
||||
except ValueError as err:
|
||||
raise ListingIterError(
|
||||
'ERROR: while fetching %s, JSON-decoding of submanifest %s '
|
||||
'while fetching %s, JSON-decoding of submanifest %s '
|
||||
'failed with %s' % (req.path, sub_req.path, err))
|
||||
|
||||
def _segment_length(self, seg_dict):
|
||||
|
@ -526,7 +526,9 @@ class SloGetContext(WSGIContext):
|
|||
# do this check here so that we can avoid fetching this last
|
||||
# manifest before raising the exception
|
||||
if recursion_depth >= self.max_slo_recursion_depth:
|
||||
raise ListingIterError("Max recursion depth exceeded")
|
||||
raise ListingIterError(
|
||||
"While processing manifest %r, "
|
||||
"max recursion depth was exceeded" % req.path)
|
||||
|
||||
sub_path = get_valid_utf8_str(seg_dict['name'])
|
||||
sub_cont, sub_obj = split_path(sub_path, 2, 2, True)
|
||||
|
@ -929,7 +931,10 @@ class StaticLargeObject(object):
|
|||
'Number of segments must be <= %d' %
|
||||
self.max_manifest_segments)
|
||||
total_size = 0
|
||||
out_content_type = req.accept.best_match(ACCEPTABLE_FORMATS)
|
||||
try:
|
||||
out_content_type = req.accept.best_match(ACCEPTABLE_FORMATS)
|
||||
except ValueError:
|
||||
out_content_type = 'text/plain' # Ignore invalid header
|
||||
if not out_content_type:
|
||||
out_content_type = 'text/plain'
|
||||
data_for_storage = []
|
||||
|
@ -948,7 +953,7 @@ class StaticLargeObject(object):
|
|||
agent='%(orig)s SLO MultipartPUT', swift_source='SLO')
|
||||
return obj_name, sub_req.get_response(self)
|
||||
|
||||
def validate_seg_dict(seg_dict, head_seg_resp):
|
||||
def validate_seg_dict(seg_dict, head_seg_resp, allow_empty_segment):
|
||||
if not head_seg_resp.is_success:
|
||||
problem_segments.append([quote(obj_name),
|
||||
head_seg_resp.status])
|
||||
|
@ -976,7 +981,7 @@ class StaticLargeObject(object):
|
|||
seg_dict['range'] = '%d-%d' % (rng[0], rng[1] - 1)
|
||||
segment_length = rng[1] - rng[0]
|
||||
|
||||
if segment_length < 1:
|
||||
if segment_length < 1 and not allow_empty_segment:
|
||||
problem_segments.append(
|
||||
[quote(obj_name),
|
||||
'Too small; each segment must be at least 1 byte.'])
|
||||
|
@ -1012,7 +1017,8 @@ class StaticLargeObject(object):
|
|||
(path, ) for path in path2indices)):
|
||||
for i in path2indices[obj_name]:
|
||||
segment_length, seg_data = validate_seg_dict(
|
||||
parsed_data[i], resp)
|
||||
parsed_data[i], resp,
|
||||
allow_empty_segment=(i == len(parsed_data) - 1))
|
||||
data_for_storage[i] = seg_data
|
||||
total_size += segment_length
|
||||
|
||||
|
@ -1154,7 +1160,10 @@ class StaticLargeObject(object):
|
|||
"""
|
||||
req.headers['Content-Type'] = None # Ignore content-type from client
|
||||
resp = HTTPOk(request=req)
|
||||
out_content_type = req.accept.best_match(ACCEPTABLE_FORMATS)
|
||||
try:
|
||||
out_content_type = req.accept.best_match(ACCEPTABLE_FORMATS)
|
||||
except ValueError:
|
||||
out_content_type = None # Ignore invalid header
|
||||
if out_content_type:
|
||||
resp.content_type = out_content_type
|
||||
resp.app_iter = self.bulk_deleter.handle_delete_iter(
|
||||
|
|
|
@ -339,7 +339,7 @@ class SegmentedIterable(object):
|
|||
seg_size is not None and last_byte == seg_size - 1)
|
||||
if time.time() - start_time > self.max_get_time:
|
||||
raise SegmentError(
|
||||
'ERROR: While processing manifest %s, '
|
||||
'While processing manifest %s, '
|
||||
'max LO GET time of %ds exceeded' %
|
||||
(self.name, self.max_get_time))
|
||||
# The "multipart-manifest=get" query param ensures that the
|
||||
|
@ -396,7 +396,7 @@ class SegmentedIterable(object):
|
|||
e_type, e_value, e_traceback = sys.exc_info()
|
||||
if time.time() - start_time > self.max_get_time:
|
||||
raise SegmentError(
|
||||
'ERROR: While processing manifest %s, '
|
||||
'While processing manifest %s, '
|
||||
'max LO GET time of %ds exceeded' %
|
||||
(self.name, self.max_get_time))
|
||||
if pending_req:
|
||||
|
@ -405,7 +405,7 @@ class SegmentedIterable(object):
|
|||
|
||||
if time.time() - start_time > self.max_get_time:
|
||||
raise SegmentError(
|
||||
'ERROR: While processing manifest %s, '
|
||||
'While processing manifest %s, '
|
||||
'max LO GET time of %ds exceeded' %
|
||||
(self.name, self.max_get_time))
|
||||
if pending_req:
|
||||
|
@ -420,7 +420,7 @@ class SegmentedIterable(object):
|
|||
if not is_success(seg_resp.status_int):
|
||||
close_if_possible(seg_resp.app_iter)
|
||||
raise SegmentError(
|
||||
'ERROR: While processing manifest %s, '
|
||||
'While processing manifest %s, '
|
||||
'got %d while retrieving %s' %
|
||||
(self.name, seg_resp.status_int, seg_req.path))
|
||||
|
||||
|
@ -485,10 +485,10 @@ class SegmentedIterable(object):
|
|||
if bytes_left:
|
||||
raise SegmentError(
|
||||
'Not enough bytes for %s; closing connection' % self.name)
|
||||
except (ListingIterError, SegmentError):
|
||||
self.logger.exception(_('ERROR: An error occurred '
|
||||
'while retrieving segments'))
|
||||
raise
|
||||
except (ListingIterError, SegmentError) as err:
|
||||
self.logger.error(err)
|
||||
if not self.validated_first_segment:
|
||||
raise
|
||||
finally:
|
||||
if self.current_resp:
|
||||
close_if_possible(self.current_resp.app_iter)
|
||||
|
@ -533,12 +533,13 @@ class SegmentedIterable(object):
|
|||
"""
|
||||
if self.validated_first_segment:
|
||||
return
|
||||
self.validated_first_segment = True
|
||||
|
||||
try:
|
||||
self.peeked_chunk = next(self.app_iter)
|
||||
except StopIteration:
|
||||
pass
|
||||
finally:
|
||||
self.validated_first_segment = True
|
||||
|
||||
def __iter__(self):
|
||||
if self.peeked_chunk is not None:
|
||||
|
|
|
@ -691,11 +691,9 @@ class Accept(object):
|
|||
Returns None if no available options are acceptable to the client.
|
||||
|
||||
:param options: a list of content-types the server can respond with
|
||||
:raises ValueError: if the header is malformed
|
||||
"""
|
||||
try:
|
||||
types = self._get_types()
|
||||
except ValueError:
|
||||
return None
|
||||
types = self._get_types()
|
||||
if not types and options:
|
||||
return options[0]
|
||||
for pattern in types:
|
||||
|
|
|
@ -1989,6 +1989,25 @@ def get_hub():
|
|||
getting swallowed somewhere. Then when that file descriptor
|
||||
was re-used, eventlet would freak right out because it still
|
||||
thought it was waiting for activity from it in some other coro.
|
||||
|
||||
Another note about epoll: it's hard to use when forking. epoll works
|
||||
like so:
|
||||
|
||||
* create an epoll instance: efd = epoll_create(...)
|
||||
|
||||
* register file descriptors of interest with epoll_ctl(efd,
|
||||
EPOLL_CTL_ADD, fd, ...)
|
||||
|
||||
* wait for events with epoll_wait(efd, ...)
|
||||
|
||||
If you fork, you and all your child processes end up using the same
|
||||
epoll instance, and everyone becomes confused. It is possible to use
|
||||
epoll and fork and still have a correct program as long as you do the
|
||||
right things, but eventlet doesn't do those things. Really, it can't
|
||||
even try to do those things since it doesn't get notified of forks.
|
||||
|
||||
In contrast, both poll() and select() specify the set of interesting
|
||||
file descriptors with each call, so there's no problem with forking.
|
||||
"""
|
||||
try:
|
||||
import select
|
||||
|
|
|
@ -26,14 +26,13 @@ import six
|
|||
import six.moves.cPickle as pickle
|
||||
import shutil
|
||||
|
||||
from eventlet import (GreenPile, GreenPool, Timeout, sleep, hubs, tpool,
|
||||
spawn)
|
||||
from eventlet import (GreenPile, GreenPool, Timeout, sleep, tpool, spawn)
|
||||
from eventlet.support.greenlets import GreenletExit
|
||||
|
||||
from swift import gettext_ as _
|
||||
from swift.common.utils import (
|
||||
whataremyips, unlink_older_than, compute_eta, get_logger,
|
||||
dump_recon_cache, mkdirs, config_true_value, list_from_csv, get_hub,
|
||||
dump_recon_cache, mkdirs, config_true_value, list_from_csv,
|
||||
tpool_reraise, GreenAsyncPile, Timestamp, remove_file)
|
||||
from swift.common.header_key_dict import HeaderKeyDict
|
||||
from swift.common.bufferedhttp import http_connect
|
||||
|
@ -51,9 +50,6 @@ from swift.common.exceptions import ConnectionTimeout, DiskFileError, \
|
|||
SYNC, REVERT = ('sync_only', 'sync_revert')
|
||||
|
||||
|
||||
hubs.use_hub(get_hub())
|
||||
|
||||
|
||||
def _get_partners(frag_index, part_nodes):
|
||||
"""
|
||||
Returns the left and right partners of the node whose index is
|
||||
|
|
|
@ -25,7 +25,7 @@ import six.moves.cPickle as pickle
|
|||
from swift import gettext_ as _
|
||||
|
||||
import eventlet
|
||||
from eventlet import GreenPool, tpool, Timeout, sleep, hubs
|
||||
from eventlet import GreenPool, tpool, Timeout, sleep
|
||||
from eventlet.green import subprocess
|
||||
from eventlet.support.greenlets import GreenletExit
|
||||
|
||||
|
@ -33,7 +33,7 @@ from swift.common.ring.utils import is_local_device
|
|||
from swift.common.utils import whataremyips, unlink_older_than, \
|
||||
compute_eta, get_logger, dump_recon_cache, ismount, \
|
||||
rsync_module_interpolation, mkdirs, config_true_value, list_from_csv, \
|
||||
get_hub, tpool_reraise, config_auto_int_value, storage_directory
|
||||
tpool_reraise, config_auto_int_value, storage_directory
|
||||
from swift.common.bufferedhttp import http_connect
|
||||
from swift.common.daemon import Daemon
|
||||
from swift.common.http import HTTP_OK, HTTP_INSUFFICIENT_STORAGE
|
||||
|
@ -43,8 +43,6 @@ from swift.common.storage_policy import POLICIES, REPL_POLICY
|
|||
|
||||
DEFAULT_RSYNC_TIMEOUT = 900
|
||||
|
||||
hubs.use_hub(get_hub())
|
||||
|
||||
|
||||
def _do_listdir(partition, replication_cycle):
|
||||
return (((partition + replication_cycle) % 10) == 0)
|
||||
|
|
|
@ -40,7 +40,7 @@ def tearDownModule():
|
|||
class TestObject(unittest2.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
if tf.skip:
|
||||
if tf.skip or tf.skip2:
|
||||
raise SkipTest
|
||||
self.container = uuid4().hex
|
||||
|
||||
|
@ -583,7 +583,7 @@ class TestObject(unittest2.TestCase):
|
|||
self.assertIn(resp.status, (204, 404))
|
||||
|
||||
def test_copy_between_accounts(self):
|
||||
if tf.skip:
|
||||
if tf.skip2:
|
||||
raise SkipTest
|
||||
|
||||
source = '%s/%s' % (self.container, self.obj)
|
||||
|
|
|
@ -713,8 +713,8 @@ def quiet_eventlet_exceptions():
|
|||
def mock_check_drive(isdir=False, ismount=False):
|
||||
"""
|
||||
All device/drive/mount checking should be done through the constraints
|
||||
module if we keep the mocking consistly w/i that module we can keep our
|
||||
test robust to further rework on that interface.
|
||||
module. If we keep the mocking consistently within that module, we can
|
||||
keep our tests robust to further rework on that interface.
|
||||
|
||||
Replace the constraint modules underlying os calls with mocks.
|
||||
|
||||
|
|
|
@ -353,6 +353,13 @@ class TestAccountController(unittest.TestCase):
|
|||
resp = req.get_response(self.controller)
|
||||
self.assertEqual(resp.status_int, 406)
|
||||
|
||||
def test_HEAD_invalid_accept(self):
|
||||
req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'HEAD'},
|
||||
headers={'Accept': 'application/plain;q=1;q=0.5'})
|
||||
resp = req.get_response(self.controller)
|
||||
self.assertEqual(resp.status_int, 400)
|
||||
self.assertEqual(resp.body, '')
|
||||
|
||||
def test_HEAD_invalid_format(self):
|
||||
format = '%D1%BD%8A9' # invalid UTF-8; should be %E1%BD%8A9 (E -> D)
|
||||
req = Request.blank('/sda1/p/a?format=' + format,
|
||||
|
@ -787,6 +794,13 @@ class TestAccountController(unittest.TestCase):
|
|||
self.assertEqual(resp.headers['Content-Type'],
|
||||
'application/xml; charset=utf-8')
|
||||
|
||||
def test_GET_invalid_accept(self):
|
||||
req = Request.blank('/sda1/p/a', environ={'REQUEST_METHOD': 'GET'},
|
||||
headers={'Accept': 'application/plain;q=foo'})
|
||||
resp = req.get_response(self.controller)
|
||||
self.assertEqual(resp.status_int, 400)
|
||||
self.assertEqual(resp.body, 'Invalid Accept header')
|
||||
|
||||
def test_GET_over_limit(self):
|
||||
req = Request.blank(
|
||||
'/sda1/p/a?limit=%d' % (constraints.ACCOUNT_LISTING_LIMIT + 1),
|
||||
|
@ -2096,8 +2110,8 @@ class TestAccountController(unittest.TestCase):
|
|||
StoragePolicy(2, 'two', False),
|
||||
StoragePolicy(3, 'three', False)])
|
||||
class TestNonLegacyDefaultStoragePolicy(TestAccountController):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -23,7 +23,7 @@ from textwrap import dedent
|
|||
import time
|
||||
import unittest
|
||||
|
||||
from swift.common import exceptions, swob
|
||||
from swift.common import swob
|
||||
from swift.common.header_key_dict import HeaderKeyDict
|
||||
from swift.common.middleware import dlo
|
||||
from swift.common.utils import closing_if_possible
|
||||
|
@ -38,7 +38,7 @@ def md5hex(s):
|
|||
|
||||
|
||||
class DloTestCase(unittest.TestCase):
|
||||
def call_dlo(self, req, app=None, expect_exception=False):
|
||||
def call_dlo(self, req, app=None):
|
||||
if app is None:
|
||||
app = self.dlo
|
||||
|
||||
|
@ -53,22 +53,11 @@ class DloTestCase(unittest.TestCase):
|
|||
|
||||
body_iter = app(req.environ, start_response)
|
||||
body = ''
|
||||
caught_exc = None
|
||||
try:
|
||||
# appease the close-checker
|
||||
with closing_if_possible(body_iter):
|
||||
for chunk in body_iter:
|
||||
body += chunk
|
||||
except Exception as exc:
|
||||
if expect_exception:
|
||||
caught_exc = exc
|
||||
else:
|
||||
raise
|
||||
|
||||
if expect_exception:
|
||||
return status[0], headers[0], body, caught_exc
|
||||
else:
|
||||
return status[0], headers[0], body
|
||||
# appease the close-checker
|
||||
with closing_if_possible(body_iter):
|
||||
for chunk in body_iter:
|
||||
body += chunk
|
||||
return status[0], headers[0], body
|
||||
|
||||
def setUp(self):
|
||||
self.app = FakeSwift()
|
||||
|
@ -561,7 +550,7 @@ class TestDloGetManifest(DloTestCase):
|
|||
environ={'REQUEST_METHOD': 'GET'},
|
||||
headers={'If-Modified-Since': 'Wed, 12 Feb 2014 22:24:52 GMT',
|
||||
'If-Unmodified-Since': 'Thu, 13 Feb 2014 23:25:53 GMT'})
|
||||
status, headers, body, exc = self.call_dlo(req, expect_exception=True)
|
||||
status, headers, body = self.call_dlo(req)
|
||||
|
||||
for _, _, hdrs in self.app.calls_with_headers[1:]:
|
||||
self.assertFalse('If-Modified-Since' in hdrs)
|
||||
|
@ -576,10 +565,10 @@ class TestDloGetManifest(DloTestCase):
|
|||
environ={'REQUEST_METHOD': 'GET'})
|
||||
status, headers, body = self.call_dlo(req)
|
||||
self.assertEqual(status, "409 Conflict")
|
||||
err_lines = self.dlo.logger.get_lines_for_level('error')
|
||||
self.assertEqual(len(err_lines), 1)
|
||||
self.assertTrue(err_lines[0].startswith(
|
||||
'ERROR: An error occurred while retrieving segments'))
|
||||
self.assertEqual(self.dlo.logger.get_lines_for_level('error'), [
|
||||
'While processing manifest /v1/AUTH_test/mancon/manifest, '
|
||||
'got 403 while retrieving /v1/AUTH_test/c/seg_01',
|
||||
])
|
||||
|
||||
def test_error_fetching_second_segment(self):
|
||||
self.app.register(
|
||||
|
@ -588,16 +577,15 @@ class TestDloGetManifest(DloTestCase):
|
|||
|
||||
req = swob.Request.blank('/v1/AUTH_test/mancon/manifest',
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
status, headers, body, exc = self.call_dlo(req, expect_exception=True)
|
||||
status, headers, body = self.call_dlo(req)
|
||||
headers = HeaderKeyDict(headers)
|
||||
|
||||
self.assertTrue(isinstance(exc, exceptions.SegmentError))
|
||||
self.assertEqual(status, "200 OK")
|
||||
self.assertEqual(''.join(body), "aaaaa") # first segment made it out
|
||||
err_lines = self.dlo.logger.get_lines_for_level('error')
|
||||
self.assertEqual(len(err_lines), 1)
|
||||
self.assertTrue(err_lines[0].startswith(
|
||||
'ERROR: An error occurred while retrieving segments'))
|
||||
self.assertEqual(self.dlo.logger.get_lines_for_level('error'), [
|
||||
'While processing manifest /v1/AUTH_test/mancon/manifest, '
|
||||
'got 403 while retrieving /v1/AUTH_test/c/seg_02',
|
||||
])
|
||||
|
||||
def test_error_listing_container_first_listing_request(self):
|
||||
self.app.register(
|
||||
|
@ -620,9 +608,7 @@ class TestDloGetManifest(DloTestCase):
|
|||
environ={'REQUEST_METHOD': 'GET'},
|
||||
headers={'Range': 'bytes=-5'})
|
||||
with mock.patch(LIMIT, 3):
|
||||
status, headers, body, exc = self.call_dlo(
|
||||
req, expect_exception=True)
|
||||
self.assertTrue(isinstance(exc, exceptions.ListingIterError))
|
||||
status, headers, body = self.call_dlo(req)
|
||||
self.assertEqual(status, "200 OK")
|
||||
self.assertEqual(body, "aaaaabbbbbccccc")
|
||||
|
||||
|
@ -634,10 +620,9 @@ class TestDloGetManifest(DloTestCase):
|
|||
|
||||
req = swob.Request.blank('/v1/AUTH_test/mancon/manifest',
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
status, headers, body, exc = self.call_dlo(req, expect_exception=True)
|
||||
status, headers, body = self.call_dlo(req)
|
||||
headers = HeaderKeyDict(headers)
|
||||
|
||||
self.assertTrue(isinstance(exc, exceptions.SegmentError))
|
||||
self.assertEqual(status, "200 OK")
|
||||
self.assertEqual(''.join(body), "aaaaabbWRONGbb") # stop after error
|
||||
|
||||
|
@ -712,12 +697,10 @@ class TestDloGetManifest(DloTestCase):
|
|||
mock.patch('swift.common.request_helpers.is_success',
|
||||
mock_is_success), \
|
||||
mock.patch.object(dlo, 'is_success', mock_is_success):
|
||||
status, headers, body, exc = self.call_dlo(
|
||||
req, expect_exception=True)
|
||||
status, headers, body = self.call_dlo(req)
|
||||
|
||||
self.assertEqual(status, '200 OK')
|
||||
self.assertEqual(body, 'aaaaabbbbbccccc')
|
||||
self.assertTrue(isinstance(exc, exceptions.SegmentError))
|
||||
|
||||
def test_get_oversize_segment(self):
|
||||
# If we send a Content-Length header to the client, it's based on the
|
||||
|
@ -735,13 +718,12 @@ class TestDloGetManifest(DloTestCase):
|
|||
req = swob.Request.blank(
|
||||
'/v1/AUTH_test/mancon/manifest',
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
status, headers, body, exc = self.call_dlo(req, expect_exception=True)
|
||||
status, headers, body = self.call_dlo(req)
|
||||
headers = HeaderKeyDict(headers)
|
||||
|
||||
self.assertEqual(status, '200 OK') # sanity check
|
||||
self.assertEqual(headers.get('Content-Length'), '25') # sanity check
|
||||
self.assertEqual(body, 'aaaaabbbbbccccccccccccccc')
|
||||
self.assertTrue(isinstance(exc, exceptions.SegmentError))
|
||||
self.assertEqual(
|
||||
self.app.calls,
|
||||
[('GET', '/v1/AUTH_test/mancon/manifest'),
|
||||
|
@ -768,13 +750,12 @@ class TestDloGetManifest(DloTestCase):
|
|||
req = swob.Request.blank(
|
||||
'/v1/AUTH_test/mancon/manifest',
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
status, headers, body, exc = self.call_dlo(req, expect_exception=True)
|
||||
status, headers, body = self.call_dlo(req)
|
||||
headers = HeaderKeyDict(headers)
|
||||
|
||||
self.assertEqual(status, '200 OK') # sanity check
|
||||
self.assertEqual(headers.get('Content-Length'), '25') # sanity check
|
||||
self.assertEqual(body, 'aaaaabbbbbccccdddddeeeee')
|
||||
self.assertTrue(isinstance(exc, exceptions.SegmentError))
|
||||
|
||||
def test_get_undersize_segment_range(self):
|
||||
# Shrink it by a single byte
|
||||
|
@ -787,13 +768,12 @@ class TestDloGetManifest(DloTestCase):
|
|||
'/v1/AUTH_test/mancon/manifest',
|
||||
environ={'REQUEST_METHOD': 'GET'},
|
||||
headers={'Range': 'bytes=0-14'})
|
||||
status, headers, body, exc = self.call_dlo(req, expect_exception=True)
|
||||
status, headers, body = self.call_dlo(req)
|
||||
headers = HeaderKeyDict(headers)
|
||||
|
||||
self.assertEqual(status, '206 Partial Content') # sanity check
|
||||
self.assertEqual(headers.get('Content-Length'), '15') # sanity check
|
||||
self.assertEqual(body, 'aaaaabbbbbcccc')
|
||||
self.assertTrue(isinstance(exc, exceptions.SegmentError))
|
||||
|
||||
def test_get_with_auth_overridden(self):
|
||||
auth_got_called = [0]
|
||||
|
|
|
@ -19,6 +19,7 @@ import mock
|
|||
import os
|
||||
from posix import stat_result, statvfs_result
|
||||
from shutil import rmtree
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest import TestCase
|
||||
|
||||
|
@ -212,9 +213,7 @@ class FakeRecon(object):
|
|||
class TestReconSuccess(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# can't use mkdtemp here as 2.6 gzip puts the filename in the header
|
||||
# which will cause ring md5 checks to fail
|
||||
self.tempdir = '/tmp/swift_recon_md5_test'
|
||||
self.tempdir = tempfile.mkdtemp(prefix='swift_recon_md5_test')
|
||||
utils.mkdirs(self.tempdir)
|
||||
self.app = self._get_app()
|
||||
self.mockos = MockOS()
|
||||
|
@ -269,21 +268,7 @@ class TestReconSuccess(TestCase):
|
|||
return app
|
||||
|
||||
def _create_ring(self, ringpath, replica_map, devs, part_shift):
|
||||
def fake_time():
|
||||
return 0
|
||||
|
||||
def fake_base(fname):
|
||||
# least common denominator with gzip versions is to
|
||||
# not use the .gz extension in the gzip header
|
||||
return fname[:-3]
|
||||
|
||||
# eliminate time from the equation as gzip 2.6 includes
|
||||
# it in the header resulting in md5 file mismatch, also
|
||||
# have to mock basename as one version uses it, one doesn't
|
||||
with mock.patch("time.time", fake_time):
|
||||
with mock.patch("os.path.basename", fake_base):
|
||||
ring.RingData(replica_map, devs, part_shift).save(ringpath,
|
||||
mtime=None)
|
||||
ring.RingData(replica_map, devs, part_shift).save(ringpath)
|
||||
|
||||
def _create_rings(self):
|
||||
# make the rings unique so they have different md5 sums
|
||||
|
|
|
@ -23,7 +23,6 @@ import unittest
|
|||
from mock import patch
|
||||
from StringIO import StringIO
|
||||
from swift.common import swob, utils
|
||||
from swift.common.exceptions import ListingIterError, SegmentError
|
||||
from swift.common.header_key_dict import HeaderKeyDict
|
||||
from swift.common.middleware import slo
|
||||
from swift.common.swob import Request, HTTPException
|
||||
|
@ -61,7 +60,7 @@ class SloTestCase(unittest.TestCase):
|
|||
self.slo = slo.filter_factory(slo_conf)(self.app)
|
||||
self.slo.logger = self.app.logger
|
||||
|
||||
def call_app(self, req, app=None, expect_exception=False):
|
||||
def call_app(self, req, app=None):
|
||||
if app is None:
|
||||
app = self.app
|
||||
|
||||
|
@ -76,22 +75,11 @@ class SloTestCase(unittest.TestCase):
|
|||
|
||||
body_iter = app(req.environ, start_response)
|
||||
body = ''
|
||||
caught_exc = None
|
||||
try:
|
||||
# appease the close-checker
|
||||
with closing_if_possible(body_iter):
|
||||
for chunk in body_iter:
|
||||
body += chunk
|
||||
except Exception as exc:
|
||||
if expect_exception:
|
||||
caught_exc = exc
|
||||
else:
|
||||
raise
|
||||
|
||||
if expect_exception:
|
||||
return status[0], headers[0], body, caught_exc
|
||||
else:
|
||||
return status[0], headers[0], body
|
||||
# appease the close-checker
|
||||
with closing_if_possible(body_iter):
|
||||
for chunk in body_iter:
|
||||
body += chunk
|
||||
return status[0], headers[0], body
|
||||
|
||||
def call_slo(self, req, **kwargs):
|
||||
return self.call_app(req, app=self.slo, **kwargs)
|
||||
|
@ -487,17 +475,17 @@ class TestSloPutManifest(SloTestCase):
|
|||
status, headers, body = self.call_slo(req)
|
||||
self.assertEqual(status, '400 Bad Request')
|
||||
|
||||
def test_handle_multipart_put_disallow_empty_last_segment(self):
|
||||
def test_handle_multipart_put_allow_empty_last_segment(self):
|
||||
test_json_data = json.dumps([{'path': '/cont/object',
|
||||
'etag': 'etagoftheobjectsegment',
|
||||
'size_bytes': 100},
|
||||
{'path': '/cont/small_object',
|
||||
{'path': '/cont/empty_object',
|
||||
'etag': 'etagoftheobjectsegment',
|
||||
'size_bytes': 0}])
|
||||
req = Request.blank('/v1/a/c/o?multipart-manifest=put',
|
||||
req = Request.blank('/v1/AUTH_test/c/man?multipart-manifest=put',
|
||||
method='PUT', body=test_json_data)
|
||||
status, headers, body = self.call_slo(req)
|
||||
self.assertEqual(status, '400 Bad Request')
|
||||
self.assertEqual(status, '201 Created')
|
||||
|
||||
def test_handle_multipart_put_success_unicode(self):
|
||||
test_json_data = json.dumps([{'path': u'/cont/object\u2661',
|
||||
|
@ -2473,15 +2461,19 @@ class TestSloGetManifest(SloTestCase):
|
|||
req = Request.blank(
|
||||
'/v1/AUTH_test/gettest/man1',
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
status, headers, body, exc = self.call_slo(req, expect_exception=True)
|
||||
status, headers, body = self.call_slo(req)
|
||||
headers = HeaderKeyDict(headers)
|
||||
|
||||
self.assertIsInstance(exc, ListingIterError)
|
||||
# we don't know at header-sending time that things are going to go
|
||||
# wrong, so we end up with a 200 and a truncated body
|
||||
self.assertEqual(status, '200 OK')
|
||||
self.assertEqual(body, ('body01body02body03body04body05' +
|
||||
'body06body07body08body09body10'))
|
||||
# but the error shows up in logs
|
||||
self.assertEqual(self.slo.logger.get_lines_for_level('error'), [
|
||||
"While processing manifest '/v1/AUTH_test/gettest/man1', "
|
||||
"max recursion depth was exceeded"
|
||||
])
|
||||
# make sure we didn't keep asking for segments
|
||||
self.assertEqual(self.app.call_count, 20)
|
||||
|
||||
|
@ -2592,10 +2584,10 @@ class TestSloGetManifest(SloTestCase):
|
|||
|
||||
self.assertEqual(status, '409 Conflict')
|
||||
self.assertEqual(self.app.call_count, 10)
|
||||
error_lines = self.slo.logger.get_lines_for_level('error')
|
||||
self.assertEqual(len(error_lines), 1)
|
||||
self.assertTrue(error_lines[0].startswith(
|
||||
'ERROR: An error occurred while retrieving segments'))
|
||||
self.assertEqual(self.slo.logger.get_lines_for_level('error'), [
|
||||
"While processing manifest '/v1/AUTH_test/gettest/man1', "
|
||||
"max recursion depth was exceeded"
|
||||
])
|
||||
|
||||
def test_get_with_if_modified_since(self):
|
||||
# It's important not to pass the If-[Un]Modified-Since header to the
|
||||
|
@ -2606,7 +2598,8 @@ class TestSloGetManifest(SloTestCase):
|
|||
environ={'REQUEST_METHOD': 'GET'},
|
||||
headers={'If-Modified-Since': 'Wed, 12 Feb 2014 22:24:52 GMT',
|
||||
'If-Unmodified-Since': 'Thu, 13 Feb 2014 23:25:53 GMT'})
|
||||
status, headers, body, exc = self.call_slo(req, expect_exception=True)
|
||||
status, headers, body = self.call_slo(req)
|
||||
self.assertEqual(self.slo.logger.get_lines_for_level('error'), [])
|
||||
|
||||
for _, _, hdrs in self.app.calls_with_headers[1:]:
|
||||
self.assertFalse('If-Modified-Since' in hdrs)
|
||||
|
@ -2619,11 +2612,14 @@ class TestSloGetManifest(SloTestCase):
|
|||
req = Request.blank(
|
||||
'/v1/AUTH_test/gettest/manifest-abcd',
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
status, headers, body, exc = self.call_slo(req, expect_exception=True)
|
||||
status, headers, body = self.call_slo(req)
|
||||
headers = HeaderKeyDict(headers)
|
||||
|
||||
self.assertIsInstance(exc, SegmentError)
|
||||
self.assertEqual(status, '200 OK')
|
||||
self.assertEqual(self.slo.logger.get_lines_for_level('error'), [
|
||||
'While processing manifest /v1/AUTH_test/gettest/manifest-abcd, '
|
||||
'got 401 while retrieving /v1/AUTH_test/gettest/c_15'
|
||||
])
|
||||
self.assertEqual(self.app.calls, [
|
||||
('GET', '/v1/AUTH_test/gettest/manifest-abcd'),
|
||||
('GET', '/v1/AUTH_test/gettest/manifest-bc'),
|
||||
|
@ -2638,11 +2634,15 @@ class TestSloGetManifest(SloTestCase):
|
|||
req = Request.blank(
|
||||
'/v1/AUTH_test/gettest/manifest-abcd',
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
status, headers, body, exc = self.call_slo(req, expect_exception=True)
|
||||
status, headers, body = self.call_slo(req)
|
||||
|
||||
self.assertIsInstance(exc, ListingIterError)
|
||||
self.assertEqual("200 OK", status)
|
||||
self.assertEqual("aaaaa", body)
|
||||
self.assertEqual(self.slo.logger.get_lines_for_level('error'), [
|
||||
'while fetching /v1/AUTH_test/gettest/manifest-abcd, GET of '
|
||||
'submanifest /v1/AUTH_test/gettest/manifest-bc failed with '
|
||||
'status 401'
|
||||
])
|
||||
self.assertEqual(self.app.calls, [
|
||||
('GET', '/v1/AUTH_test/gettest/manifest-abcd'),
|
||||
# This one has the error, and so is the last one we fetch.
|
||||
|
@ -2672,10 +2672,11 @@ class TestSloGetManifest(SloTestCase):
|
|||
status, headers, body = self.call_slo(req)
|
||||
|
||||
self.assertEqual('409 Conflict', status)
|
||||
error_lines = self.slo.logger.get_lines_for_level('error')
|
||||
self.assertEqual(len(error_lines), 1)
|
||||
self.assertTrue(error_lines[0].startswith(
|
||||
'ERROR: An error occurred while retrieving segments'))
|
||||
self.assertEqual(self.slo.logger.get_lines_for_level('error'), [
|
||||
'while fetching /v1/AUTH_test/gettest/manifest-manifest-a, GET '
|
||||
'of submanifest /v1/AUTH_test/gettest/manifest-a failed with '
|
||||
'status 403'
|
||||
])
|
||||
|
||||
def test_invalid_json_submanifest(self):
|
||||
self.app.register(
|
||||
|
@ -2688,11 +2689,15 @@ class TestSloGetManifest(SloTestCase):
|
|||
req = Request.blank(
|
||||
'/v1/AUTH_test/gettest/manifest-abcd',
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
status, headers, body, exc = self.call_slo(req, expect_exception=True)
|
||||
status, headers, body = self.call_slo(req)
|
||||
|
||||
self.assertIsInstance(exc, ListingIterError)
|
||||
self.assertEqual('200 OK', status)
|
||||
self.assertEqual(body, 'aaaaa')
|
||||
self.assertEqual(self.slo.logger.get_lines_for_level('error'), [
|
||||
'while fetching /v1/AUTH_test/gettest/manifest-abcd, '
|
||||
'JSON-decoding of submanifest /v1/AUTH_test/gettest/manifest-bc '
|
||||
'failed with No JSON object could be decoded'
|
||||
])
|
||||
|
||||
def test_mismatched_etag(self):
|
||||
self.app.register(
|
||||
|
@ -2709,11 +2714,14 @@ class TestSloGetManifest(SloTestCase):
|
|||
req = Request.blank(
|
||||
'/v1/AUTH_test/gettest/manifest-a-b-badetag-c',
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
status, headers, body, exc = self.call_slo(req, expect_exception=True)
|
||||
status, headers, body = self.call_slo(req)
|
||||
|
||||
self.assertIsInstance(exc, SegmentError)
|
||||
self.assertEqual('200 OK', status)
|
||||
self.assertEqual(body, 'aaaaa')
|
||||
self.assertEqual(self.slo.logger.get_lines_for_level('error'), [
|
||||
'Object segment no longer valid: /v1/AUTH_test/gettest/b_10 '
|
||||
'etag: 82136b4240d6ce4ea7d03e51469a393b != wrong! or 10 != 10.'
|
||||
])
|
||||
|
||||
def test_mismatched_size(self):
|
||||
self.app.register(
|
||||
|
@ -2730,11 +2738,15 @@ class TestSloGetManifest(SloTestCase):
|
|||
req = Request.blank(
|
||||
'/v1/AUTH_test/gettest/manifest-a-b-badsize-c',
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
status, headers, body, exc = self.call_slo(req, expect_exception=True)
|
||||
status, headers, body = self.call_slo(req)
|
||||
|
||||
self.assertIsInstance(exc, SegmentError)
|
||||
self.assertEqual('200 OK', status)
|
||||
self.assertEqual(body, 'aaaaa')
|
||||
self.assertEqual(self.slo.logger.get_lines_for_level('error'), [
|
||||
'Object segment no longer valid: /v1/AUTH_test/gettest/b_10 '
|
||||
'etag: 82136b4240d6ce4ea7d03e51469a393b != '
|
||||
'82136b4240d6ce4ea7d03e51469a393b or 10 != 999999.'
|
||||
])
|
||||
|
||||
def test_first_segment_mismatched_etag(self):
|
||||
self.app.register('GET', '/v1/AUTH_test/gettest/manifest-badetag',
|
||||
|
@ -2750,10 +2762,10 @@ class TestSloGetManifest(SloTestCase):
|
|||
status, headers, body = self.call_slo(req)
|
||||
|
||||
self.assertEqual('409 Conflict', status)
|
||||
error_lines = self.slo.logger.get_lines_for_level('error')
|
||||
self.assertEqual(len(error_lines), 1)
|
||||
self.assertTrue(error_lines[0].startswith(
|
||||
'ERROR: An error occurred while retrieving segments'))
|
||||
self.assertEqual(self.slo.logger.get_lines_for_level('error'), [
|
||||
'Object segment no longer valid: /v1/AUTH_test/gettest/a_5 '
|
||||
'etag: 594f803b380a41396ed63dca39503542 != wrong! or 5 != 5.'
|
||||
])
|
||||
|
||||
def test_first_segment_mismatched_size(self):
|
||||
self.app.register('GET', '/v1/AUTH_test/gettest/manifest-badsize',
|
||||
|
@ -2769,10 +2781,11 @@ class TestSloGetManifest(SloTestCase):
|
|||
status, headers, body = self.call_slo(req)
|
||||
|
||||
self.assertEqual('409 Conflict', status)
|
||||
error_lines = self.slo.logger.get_lines_for_level('error')
|
||||
self.assertEqual(len(error_lines), 1)
|
||||
self.assertTrue(error_lines[0].startswith(
|
||||
'ERROR: An error occurred while retrieving segments'))
|
||||
self.assertEqual(self.slo.logger.get_lines_for_level('error'), [
|
||||
'Object segment no longer valid: /v1/AUTH_test/gettest/a_5 '
|
||||
'etag: 594f803b380a41396ed63dca39503542 != '
|
||||
'594f803b380a41396ed63dca39503542 or 5 != 999999.'
|
||||
])
|
||||
|
||||
@patch('swift.common.request_helpers.time')
|
||||
def test_download_takes_too_long(self, mock_time):
|
||||
|
@ -2791,11 +2804,13 @@ class TestSloGetManifest(SloTestCase):
|
|||
'/v1/AUTH_test/gettest/manifest-abcd',
|
||||
environ={'REQUEST_METHOD': 'GET'})
|
||||
|
||||
status, headers, body, exc = self.call_slo(
|
||||
req, expect_exception=True)
|
||||
status, headers, body = self.call_slo(req)
|
||||
|
||||
self.assertIsInstance(exc, SegmentError)
|
||||
self.assertEqual(status, '200 OK')
|
||||
self.assertEqual(self.slo.logger.get_lines_for_level('error'), [
|
||||
'While processing manifest /v1/AUTH_test/gettest/manifest-abcd, '
|
||||
'max LO GET time of 86400s exceeded'
|
||||
])
|
||||
self.assertEqual(self.app.calls, [
|
||||
('GET', '/v1/AUTH_test/gettest/manifest-abcd'),
|
||||
('GET', '/v1/AUTH_test/gettest/manifest-bc'),
|
||||
|
@ -2820,10 +2835,11 @@ class TestSloGetManifest(SloTestCase):
|
|||
status, headers, body = self.call_slo(req)
|
||||
|
||||
self.assertEqual('409 Conflict', status)
|
||||
error_lines = self.slo.logger.get_lines_for_level('error')
|
||||
self.assertEqual(len(error_lines), 1)
|
||||
self.assertTrue(error_lines[0].startswith(
|
||||
'ERROR: An error occurred while retrieving segments'))
|
||||
self.assertEqual(self.slo.logger.get_lines_for_level('error'), [
|
||||
'While processing manifest /v1/AUTH_test/gettest/'
|
||||
'manifest-not-exists, got 404 while retrieving /v1/AUTH_test/'
|
||||
'gettest/not_exists_obj'
|
||||
])
|
||||
|
||||
|
||||
class TestSloConditionalGetOldManifest(SloTestCase):
|
||||
|
|
|
@ -139,13 +139,16 @@ class TestRunDaemon(unittest.TestCase):
|
|||
|
||||
def test_run_daemon(self):
|
||||
sample_conf = "[my-daemon]\nuser = %s\n" % getuser()
|
||||
with tmpfile(sample_conf) as conf_file:
|
||||
with mock.patch.dict('os.environ', {'TZ': ''}):
|
||||
with mock.patch('time.tzset') as mock_tzset:
|
||||
daemon.run_daemon(MyDaemon, conf_file)
|
||||
self.assertTrue(MyDaemon.forever_called)
|
||||
self.assertEqual(os.environ['TZ'], 'UTC+0')
|
||||
self.assertEqual(mock_tzset.mock_calls, [mock.call()])
|
||||
with tmpfile(sample_conf) as conf_file, \
|
||||
mock.patch('swift.common.daemon.use_hub') as mock_use_hub:
|
||||
with mock.patch.dict('os.environ', {'TZ': ''}), \
|
||||
mock.patch('time.tzset') as mock_tzset:
|
||||
daemon.run_daemon(MyDaemon, conf_file)
|
||||
self.assertTrue(MyDaemon.forever_called)
|
||||
self.assertEqual(os.environ['TZ'], 'UTC+0')
|
||||
self.assertEqual(mock_tzset.mock_calls, [mock.call()])
|
||||
self.assertEqual(mock_use_hub.mock_calls,
|
||||
[mock.call(utils.get_hub())])
|
||||
daemon.run_daemon(MyDaemon, conf_file, once=True)
|
||||
self.assertEqual(MyDaemon.once_called, True)
|
||||
|
||||
|
@ -182,7 +185,8 @@ class TestRunDaemon(unittest.TestCase):
|
|||
self.assertEqual(18000, time.timezone)
|
||||
|
||||
sample_conf = "[my-daemon]\nuser = %s\n" % getuser()
|
||||
with tmpfile(sample_conf) as conf_file:
|
||||
with tmpfile(sample_conf) as conf_file, \
|
||||
mock.patch('swift.common.daemon.use_hub'):
|
||||
daemon.run_daemon(MyDaemon, conf_file)
|
||||
self.assertFalse(MyDaemon.once_called)
|
||||
self.assertTrue(MyDaemon.forever_called)
|
||||
|
|
|
@ -358,11 +358,11 @@ class TestAccept(unittest.TestCase):
|
|||
'text /plain', 'text\x7f/plain',
|
||||
'text/plain;a=b=c',
|
||||
'text/plain;q=1;q=2',
|
||||
'text/plain;q=not-a-number',
|
||||
'text/plain; ubq="unbalanced " quotes"'):
|
||||
acc = swift.common.swob.Accept(accept)
|
||||
match = acc.best_match(['text/plain', 'application/xml',
|
||||
'text/xml'])
|
||||
self.assertIsNone(match)
|
||||
with self.assertRaises(ValueError):
|
||||
acc.best_match(['text/plain', 'application/xml', 'text/xml'])
|
||||
|
||||
def test_repr(self):
|
||||
acc = swift.common.swob.Accept("application/json")
|
||||
|
|
|
@ -30,6 +30,7 @@ import logging
|
|||
import platform
|
||||
import os
|
||||
import mock
|
||||
import pwd
|
||||
import random
|
||||
import re
|
||||
import socket
|
||||
|
@ -70,7 +71,8 @@ from swift.common.container_sync_realms import ContainerSyncRealms
|
|||
from swift.common.header_key_dict import HeaderKeyDict
|
||||
from swift.common.storage_policy import POLICIES, reload_storage_policies
|
||||
from swift.common.swob import Request, Response
|
||||
from test.unit import FakeLogger, requires_o_tmpfile_support
|
||||
from test.unit import FakeLogger, requires_o_tmpfile_support, \
|
||||
quiet_eventlet_exceptions
|
||||
|
||||
threading = eventlet.patcher.original('threading')
|
||||
|
||||
|
@ -102,10 +104,10 @@ class MockOs(object):
|
|||
setgroups = chdir = setsid = setgid = setuid = umask = pass_func
|
||||
|
||||
def called_func(self, name, *args, **kwargs):
|
||||
self.called_funcs[name] = True
|
||||
self.called_funcs[name] = args
|
||||
|
||||
def raise_func(self, name, *args, **kwargs):
|
||||
self.called_funcs[name] = True
|
||||
self.called_funcs[name] = args
|
||||
raise OSError()
|
||||
|
||||
def dup2(self, source, target):
|
||||
|
@ -2131,46 +2133,51 @@ log_name = %(yarr)s'''
|
|||
}
|
||||
self.assertEqual(conf, expected)
|
||||
|
||||
def test_drop_privileges(self):
|
||||
def _check_drop_privileges(self, mock_os, required_func_calls,
|
||||
call_setsid=True):
|
||||
user = getuser()
|
||||
user_data = pwd.getpwnam(user)
|
||||
self.assertFalse(mock_os.called_funcs) # sanity check
|
||||
# over-ride os with mock
|
||||
with mock.patch('swift.common.utils.os', mock_os):
|
||||
# exercise the code
|
||||
utils.drop_privileges(user, call_setsid=call_setsid)
|
||||
|
||||
for func in required_func_calls:
|
||||
self.assertIn(func, mock_os.called_funcs)
|
||||
self.assertEqual(user_data[5], mock_os.environ['HOME'])
|
||||
groups = {g.gr_gid for g in grp.getgrall() if user in g.gr_mem}
|
||||
self.assertEqual(groups, set(mock_os.called_funcs['setgroups'][0]))
|
||||
self.assertEqual(user_data[3], mock_os.called_funcs['setgid'][0])
|
||||
self.assertEqual(user_data[2], mock_os.called_funcs['setuid'][0])
|
||||
self.assertEqual('/', mock_os.called_funcs['chdir'][0])
|
||||
self.assertEqual(0o22, mock_os.called_funcs['umask'][0])
|
||||
|
||||
def test_drop_privileges(self):
|
||||
required_func_calls = ('setgroups', 'setgid', 'setuid', 'setsid',
|
||||
'chdir', 'umask')
|
||||
utils.os = MockOs(called_funcs=required_func_calls)
|
||||
# exercise the code
|
||||
utils.drop_privileges(user)
|
||||
for func in required_func_calls:
|
||||
self.assertTrue(utils.os.called_funcs[func])
|
||||
import pwd
|
||||
self.assertEqual(pwd.getpwnam(user)[5], utils.os.environ['HOME'])
|
||||
mock_os = MockOs(called_funcs=required_func_calls)
|
||||
self._check_drop_privileges(mock_os, required_func_calls)
|
||||
|
||||
groups = [g.gr_gid for g in grp.getgrall() if user in g.gr_mem]
|
||||
groups.append(pwd.getpwnam(user).pw_gid)
|
||||
self.assertEqual(set(groups), set(os.getgroups()))
|
||||
|
||||
# reset; test same args, OSError trying to get session leader
|
||||
utils.os = MockOs(called_funcs=required_func_calls,
|
||||
raise_funcs=('setsid',))
|
||||
for func in required_func_calls:
|
||||
self.assertFalse(utils.os.called_funcs.get(func, False))
|
||||
utils.drop_privileges(user)
|
||||
for func in required_func_calls:
|
||||
self.assertTrue(utils.os.called_funcs[func])
|
||||
def test_drop_privileges_setsid_error(self):
|
||||
# OSError trying to get session leader
|
||||
required_func_calls = ('setgroups', 'setgid', 'setuid', 'setsid',
|
||||
'chdir', 'umask')
|
||||
mock_os = MockOs(called_funcs=required_func_calls,
|
||||
raise_funcs=('setsid',))
|
||||
self._check_drop_privileges(mock_os, required_func_calls)
|
||||
|
||||
def test_drop_privileges_no_call_setsid(self):
|
||||
user = getuser()
|
||||
# over-ride os with mock
|
||||
required_func_calls = ('setgroups', 'setgid', 'setuid', 'chdir',
|
||||
'umask')
|
||||
# OSError if trying to get session leader, but it shouldn't be called
|
||||
bad_func_calls = ('setsid',)
|
||||
utils.os = MockOs(called_funcs=required_func_calls,
|
||||
raise_funcs=bad_func_calls)
|
||||
# exercise the code
|
||||
utils.drop_privileges(user, call_setsid=False)
|
||||
for func in required_func_calls:
|
||||
self.assertTrue(utils.os.called_funcs[func])
|
||||
mock_os = MockOs(called_funcs=required_func_calls,
|
||||
raise_funcs=bad_func_calls)
|
||||
self._check_drop_privileges(mock_os, required_func_calls,
|
||||
call_setsid=False)
|
||||
for func in bad_func_calls:
|
||||
self.assertNotIn(func, utils.os.called_funcs)
|
||||
self.assertNotIn(func, mock_os.called_funcs)
|
||||
|
||||
@reset_logger_state
|
||||
def test_capture_stdio(self):
|
||||
|
@ -6305,8 +6312,9 @@ class TestPipeMutex(unittest.TestCase):
|
|||
|
||||
def test_wrong_releaser(self):
|
||||
self.mutex.acquire()
|
||||
self.assertRaises(RuntimeError,
|
||||
eventlet.spawn(self.mutex.release).wait)
|
||||
with quiet_eventlet_exceptions():
|
||||
self.assertRaises(RuntimeError,
|
||||
eventlet.spawn(self.mutex.release).wait)
|
||||
|
||||
def test_blocking(self):
|
||||
evt = eventlet.event.Event()
|
||||
|
|
|
@ -312,6 +312,14 @@ class TestContainerController(unittest.TestCase):
|
|||
resp = req.get_response(self.controller)
|
||||
self.assertEqual(resp.status_int, 406)
|
||||
|
||||
def test_HEAD_invalid_accept(self):
|
||||
req = Request.blank(
|
||||
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'HEAD'},
|
||||
headers={'Accept': 'application/plain;q'})
|
||||
resp = req.get_response(self.controller)
|
||||
self.assertEqual(resp.status_int, 400)
|
||||
self.assertEqual(resp.body, '')
|
||||
|
||||
def test_HEAD_invalid_format(self):
|
||||
format = '%D1%BD%8A9' # invalid UTF-8; should be %E1%BD%8A9 (E -> D)
|
||||
req = Request.blank(
|
||||
|
@ -2367,6 +2375,14 @@ class TestContainerController(unittest.TestCase):
|
|||
self.assertEqual(resp.content_type, 'text/xml')
|
||||
self.assertEqual(resp.body, xml_body)
|
||||
|
||||
def test_GET_invalid_accept(self):
|
||||
req = Request.blank(
|
||||
'/sda1/p/a/c', environ={'REQUEST_METHOD': 'GET'},
|
||||
headers={'Accept': 'application/plain;q'})
|
||||
resp = req.get_response(self.controller)
|
||||
self.assertEqual(resp.status_int, 400)
|
||||
self.assertEqual(resp.body, 'Invalid Accept header')
|
||||
|
||||
def test_GET_marker(self):
|
||||
# make a container
|
||||
req = Request.blank(
|
||||
|
|
|
@ -9271,6 +9271,24 @@ class TestAccountControllerFakeGetResponse(unittest.TestCase):
|
|||
resp = req.get_response(self.app)
|
||||
self.assertEqual(406, resp.status_int)
|
||||
|
||||
def test_GET_autocreate_bad_accept(self):
|
||||
with save_globals():
|
||||
set_http_connect(*([404] * 100)) # nonexistent: all backends 404
|
||||
req = Request.blank('/v1/a', headers={"Accept": "a/b;q=nope"},
|
||||
environ={'REQUEST_METHOD': 'GET',
|
||||
'PATH_INFO': '/v1/a'})
|
||||
resp = req.get_response(self.app)
|
||||
self.assertEqual(400, resp.status_int)
|
||||
self.assertEqual('Invalid Accept header', resp.body)
|
||||
|
||||
set_http_connect(*([404] * 100)) # nonexistent: all backends 404
|
||||
req = Request.blank('/v1/a', headers={"Accept": "a/b;q=0.5;q=1"},
|
||||
environ={'REQUEST_METHOD': 'GET',
|
||||
'PATH_INFO': '/v1/a'})
|
||||
resp = req.get_response(self.app)
|
||||
self.assertEqual(400, resp.status_int)
|
||||
self.assertEqual('Invalid Accept header', resp.body)
|
||||
|
||||
def test_GET_autocreate_format_invalid_utf8(self):
|
||||
with save_globals():
|
||||
set_http_connect(*([404] * 100)) # nonexistent: all backends 404
|
||||
|
|
Loading…
Reference in New Issue