Merge "Fix misnamed dictionary key."

This commit is contained in:
Jenkins 2014-12-15 19:02:23 +00:00 committed by Gerrit Code Review
commit 7980704c33
4 changed files with 328 additions and 72 deletions

@ -1530,8 +1530,8 @@ class SwiftService(object):
old_manifest = None
old_slo_manifest_paths = []
new_slo_manifest_paths = set()
if options['changed'] or options['skip_identical'] \
or not options['leave_segments']:
if (options['changed'] or options['skip_identical']
or not options['leave_segments']):
checksum = None
if options['skip_identical']:
try:
@ -1556,11 +1556,12 @@ class SwiftService(object):
'status': 'skipped-identical'
})
return res
cl = int(headers.get('content-length'))
mt = headers.get('x-object-meta-mtime')
if path is not None and options['changed']\
and cl == getsize(path) and \
mt == put_headers['x-object-meta-mtime']:
if (path is not None and options['changed']
and cl == getsize(path)
and mt == put_headers['x-object-meta-mtime']):
res.update({
'success': True,
'status': 'skipped-changed'
@ -1594,8 +1595,8 @@ class SwiftService(object):
# a segment job if we're reading from a stream - we may fail if we
# go over the single object limit, but this gives us a nice way
# to create objects from memory
if path is not None and options['segment_size'] and \
getsize(path) > int(options['segment_size']):
if (path is not None and options['segment_size']
and getsize(path) > int(options['segment_size'])):
res['large_object'] = True
seg_container = container + '_segments'
if options['segment_container']:
@ -1851,9 +1852,8 @@ class SwiftService(object):
# Cancel the remaining container deletes, but yield
# any pending results
if not cancelled and \
options['fail_fast'] and \
not res['success']:
if (not cancelled and options['fail_fast']
and not res['success']):
cancelled = True
@staticmethod
@ -1861,24 +1861,17 @@ class SwiftService(object):
results_dict = {}
try:
conn.delete_object(container, obj, response_dict=results_dict)
res = {
'action': 'delete_segment',
'container': container,
'object': obj,
'success': True,
'attempts': conn.attempts,
'response_dict': results_dict
}
res = {'success': True}
except Exception as e:
res = {
'action': 'delete_segment',
'container': container,
'object': obj,
'success': False,
'attempts': conn.attempts,
'response_dict': results_dict,
'exception': e
}
res = {'success': False, 'error': e}
res.update({
'action': 'delete_segment',
'container': container,
'object': obj,
'attempts': conn.attempts,
'response_dict': results_dict
})
if results_queue is not None:
results_queue.put(res)
@ -1899,8 +1892,7 @@ class SwiftService(object):
try:
headers = conn.head_object(container, obj)
old_manifest = headers.get('x-object-manifest')
if config_true_value(
headers.get('x-static-large-object')):
if config_true_value(headers.get('x-static-large-object')):
query_string = 'multipart-manifest=delete'
except ClientException as err:
if err.http_status != 404:
@ -1958,23 +1950,17 @@ class SwiftService(object):
results_dict = {}
try:
conn.delete_container(container, response_dict=results_dict)
res = {
'action': 'delete_container',
'container': container,
'object': None,
'success': True,
'attempts': conn.attempts,
'response_dict': results_dict
}
res = {'success': True}
except Exception as e:
res = {
'action': 'delete_container',
'container': container,
'object': None,
'success': False,
'response_dict': results_dict,
'error': e
}
res = {'success': False, 'error': e}
res.update({
'action': 'delete_container',
'container': container,
'object': None,
'attempts': conn.attempts,
'response_dict': results_dict
})
return res
def _delete_container(self, container, options):
@ -1982,9 +1968,7 @@ class SwiftService(object):
objs = []
for part in self.list(container=container):
if part["success"]:
objs.extend([
o['name'] for o in part['listing']
])
objs.extend([o['name'] for o in part['listing']])
else:
raise part["error"]

@ -118,34 +118,29 @@ def st_delete(parser, args, output_manager):
del_iter = swift.delete(container=container)
for r in del_iter:
c = r.get('container', '')
o = r.get('object', '')
a = r.get('attempts')
if r['success']:
if options.verbose:
a = ' [after {0} attempts]'.format(a) if a > 1 else ''
if r['action'] == 'delete_object':
c = r['container']
o = r['object']
p = '%s/%s' % (c, o) if options.yes_all else o
a = r['attempts']
if a > 1:
output_manager.print_msg(
'%s [after %d attempts]', p, a)
if options.yes_all:
p = '{0}/{1}'.format(c, o)
else:
output_manager.print_msg(p)
p = o
elif r['action'] == 'delete_segment':
c = r['container']
o = r['object']
p = '%s/%s' % (c, o)
a = r['attempts']
if a > 1:
output_manager.print_msg(
'%s [after %d attempts]', p, a)
else:
output_manager.print_msg(p)
p = '{0}/{1}'.format(c, o)
elif r['action'] == 'delete_container':
p = c
output_manager.print_msg('{0}{1}'.format(p, a))
else:
# Special case error prints
output_manager.error("An unexpected error occurred whilst "
"deleting: %s" % r['error'])
p = '{0}/{1}'.format(c, o) if o else c
output_manager.error('Error Deleting: {0}: {1}'
.format(p, r['error']))
except SwiftError as err:
output_manager.error(err.value)

@ -12,12 +12,15 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import mock
import testtools
from mock import Mock, PropertyMock
from six.moves.queue import Queue, Empty as QueueEmptyError
from hashlib import md5
from swiftclient.service import SwiftService, SwiftError
import swiftclient
from swiftclient.service import SwiftService, SwiftError
from swiftclient.client import Connection
class TestSwiftPostObject(testtools.TestCase):
@ -59,7 +62,7 @@ class TestSwiftReader(testtools.TestCase):
self.assertTrue(isinstance(sr._actual_md5, self.md5_type))
def test_create_with_large_object_headers(self):
# md5 should not be initalized if large object headers are present
# md5 should not be initialized if large object headers are present
sr = self.sr('path', 'body', {'x-object-manifest': 'test'})
self.assertEqual(sr._path, 'path')
self.assertEqual(sr._body, 'body')
@ -129,6 +132,225 @@ class TestSwiftReader(testtools.TestCase):
'97ac82a5b825239e782d0339e2d7b910')
class TestServiceDelete(testtools.TestCase):
def setUp(self):
super(TestServiceDelete, self).setUp()
self.opts = {'leave_segments': False, 'yes_all': False}
self.exc = Exception('test_exc')
# Base response to be copied and updated to matched the expected
# response for each test
self.expected = {
'action': None, # Should be string in the form delete_XX
'container': 'test_c',
'object': 'test_o',
'attempts': 2,
'response_dict': {},
'success': None # Should be a bool
}
def _get_mock_connection(self, attempts=2):
m = Mock(spec=Connection)
type(m).attempts = PropertyMock(return_value=attempts)
return m
def _get_queue(self, q):
# Instead of blocking pull items straight from the queue.
# expects at least one item otherwise the test will fail.
try:
return q.get_nowait()
except QueueEmptyError:
self.fail('Expected item in queue but found none')
def _get_expected(self, update=None):
expected = self.expected.copy()
if update:
expected.update(update)
return expected
def _assertDictEqual(self, a, b, m=None):
# assertDictEqual is not available in py2.6 so use a shallow check
# instead
if hasattr(self, 'assertDictEqual'):
self.assertDictEqual(a, b, m)
else:
self.assertTrue(isinstance(a, dict))
self.assertTrue(isinstance(b, dict))
self.assertEqual(len(a), len(b), m)
for k, v in a.items():
self.assertTrue(k in b, m)
self.assertEqual(b[k], v, m)
def test_delete_segment(self):
mock_q = Queue()
mock_conn = self._get_mock_connection()
expected_r = self._get_expected({
'action': 'delete_segment',
'object': 'test_s',
'success': True,
})
r = SwiftService._delete_segment(mock_conn, 'test_c', 'test_s', mock_q)
mock_conn.delete_object.assert_called_once_with(
'test_c', 'test_s', response_dict={}
)
self._assertDictEqual(expected_r, r)
self._assertDictEqual(expected_r, self._get_queue(mock_q))
def test_delete_segment_exception(self):
mock_q = Queue()
mock_conn = self._get_mock_connection()
mock_conn.delete_object = Mock(side_effect=self.exc)
expected_r = self._get_expected({
'action': 'delete_segment',
'object': 'test_s',
'success': False,
'error': self.exc
})
r = SwiftService._delete_segment(mock_conn, 'test_c', 'test_s', mock_q)
mock_conn.delete_object.assert_called_once_with(
'test_c', 'test_s', response_dict={}
)
self._assertDictEqual(expected_r, r)
self._assertDictEqual(expected_r, self._get_queue(mock_q))
def test_delete_object(self):
mock_q = Queue()
mock_conn = self._get_mock_connection()
mock_conn.head_object = Mock(return_value={})
expected_r = self._get_expected({
'action': 'delete_object',
'success': True
})
s = SwiftService()
r = s._delete_object(mock_conn, 'test_c', 'test_o', self.opts, mock_q)
mock_conn.head_object.assert_called_once_with('test_c', 'test_o')
mock_conn.delete_object.assert_called_once_with(
'test_c', 'test_o', query_string=None, response_dict={}
)
self._assertDictEqual(expected_r, r)
def test_delete_object_exception(self):
mock_q = Queue()
mock_conn = self._get_mock_connection()
mock_conn.delete_object = Mock(side_effect=self.exc)
expected_r = self._get_expected({
'action': 'delete_object',
'success': False,
'error': self.exc
})
# _delete_object doesnt populate attempts or response dict if it hits
# an error. This may not be the correct behaviour.
del expected_r['response_dict'], expected_r['attempts']
s = SwiftService()
r = s._delete_object(mock_conn, 'test_c', 'test_o', self.opts, mock_q)
mock_conn.head_object.assert_called_once_with('test_c', 'test_o')
mock_conn.delete_object.assert_called_once_with(
'test_c', 'test_o', query_string=None, response_dict={}
)
self._assertDictEqual(expected_r, r)
def test_delete_object_slo_support(self):
# If SLO headers are present the delete call should include an
# additional query string to cause the right delete server side
mock_q = Queue()
mock_conn = self._get_mock_connection()
mock_conn.head_object = Mock(
return_value={'x-static-large-object': True}
)
expected_r = self._get_expected({
'action': 'delete_object',
'success': True
})
s = SwiftService()
r = s._delete_object(mock_conn, 'test_c', 'test_o', self.opts, mock_q)
mock_conn.head_object.assert_called_once_with('test_c', 'test_o')
mock_conn.delete_object.assert_called_once_with(
'test_c', 'test_o',
query_string='multipart-manifest=delete',
response_dict={}
)
self._assertDictEqual(expected_r, r)
def test_delete_object_dlo_support(self):
mock_q = Queue()
s = SwiftService()
mock_conn = self._get_mock_connection()
expected_r = self._get_expected({
'action': 'delete_object',
'success': True,
'dlo_segments_deleted': True
})
# A DLO object is determined in _delete_object by heading the object
# and checking for the existence of a x-object-manifest header.
# Mock that here.
mock_conn.head_object = Mock(
return_value={'x-object-manifest': 'manifest_c/manifest_p'}
)
mock_conn.get_container = Mock(
side_effect=[(None, [{'name': 'test_seg_1'},
{'name': 'test_seg_2'}]),
(None, {})]
)
def get_mock_list_conn(options):
return mock_conn
with mock.patch('swiftclient.service.get_conn', get_mock_list_conn):
r = s._delete_object(
mock_conn, 'test_c', 'test_o', self.opts, mock_q
)
self._assertDictEqual(expected_r, r)
expected = [
mock.call('test_c', 'test_o', query_string=None, response_dict={}),
mock.call('manifest_c', 'test_seg_1', response_dict={}),
mock.call('manifest_c', 'test_seg_2', response_dict={})]
mock_conn.delete_object.assert_has_calls(expected, any_order=True)
def test_delete_empty_container(self):
mock_conn = self._get_mock_connection()
expected_r = self._get_expected({
'action': 'delete_container',
'success': True,
'object': None
})
r = SwiftService._delete_empty_container(mock_conn, 'test_c')
mock_conn.delete_container.assert_called_once_with(
'test_c', response_dict={}
)
self._assertDictEqual(expected_r, r)
def test_delete_empty_container_excpetion(self):
mock_conn = self._get_mock_connection()
mock_conn.delete_container = Mock(side_effect=self.exc)
expected_r = self._get_expected({
'action': 'delete_container',
'success': False,
'object': None,
'error': self.exc
})
s = SwiftService()
r = s._delete_empty_container(mock_conn, 'test_c')
mock_conn.delete_container.assert_called_once_with(
'test_c', response_dict={}
)
self._assertDictEqual(expected_r, r)
class TestService(testtools.TestCase):
def test_upload_with_bad_segment_size(self):

@ -387,6 +387,61 @@ class TestShell(unittest.TestCase):
connection.return_value.delete_object.assert_called_with(
'container', 'object', query_string=None, response_dict={})
def test_delete_verbose_output(self):
del_obj_res = {'success': True, 'response_dict': {}, 'attempts': 2,
'container': 'test_c', 'action': 'delete_object',
'object': 'test_o'}
del_seg_res = del_obj_res.copy()
del_seg_res.update({'action': 'delete_segment'})
del_con_res = del_obj_res.copy()
del_con_res.update({'action': 'delete_container', 'object': None})
test_exc = Exception('test_exc')
error_res = del_obj_res.copy()
error_res.update({'success': False, 'error': test_exc, 'object': None})
mock_delete = mock.Mock()
base_argv = ['', '--verbose', 'delete']
with mock.patch('swiftclient.shell.SwiftService.delete', mock_delete):
with CaptureOutput() as out:
mock_delete.return_value = [del_obj_res]
swiftclient.shell.main(base_argv + ['test_c', 'test_o'])
mock_delete.assert_called_once_with(container='test_c',
objects=['test_o'])
self.assertTrue(out.out.find(
'test_o [after 2 attempts]') >= 0)
with CaptureOutput() as out:
mock_delete.return_value = [del_seg_res]
swiftclient.shell.main(base_argv + ['test_c', 'test_o'])
mock_delete.assert_called_with(container='test_c',
objects=['test_o'])
self.assertTrue(out.out.find(
'test_c/test_o [after 2 attempts]') >= 0)
with CaptureOutput() as out:
mock_delete.return_value = [del_con_res]
swiftclient.shell.main(base_argv + ['test_c'])
mock_delete.assert_called_with(container='test_c')
self.assertTrue(out.out.find(
'test_c [after 2 attempts]') >= 0)
with CaptureOutput() as out:
mock_delete.return_value = [error_res]
self.assertRaises(SystemExit,
swiftclient.shell.main,
base_argv + ['test_c'])
mock_delete.assert_called_with(container='test_c')
self.assertTrue(out.err.find(
'Error Deleting: test_c: test_exc') >= 0)
@mock.patch('swiftclient.service.Connection')
def test_post_account(self, connection):
argv = ["", "post"]