Add an option: disable etag check on downloads

This patch is to add an option of disable etag
check on downloads.

Change-Id: I9ad389dd691942dea6db470ca3f0543eb6e9703e
Closes-bug: #1581147
This commit is contained in:
Cheng Li 2016-05-31 14:32:58 +08:00
parent f9d0657e70
commit 69bf4634b9
7 changed files with 43 additions and 5 deletions

@ -86,6 +86,7 @@ container, or a list of objects depending on the args given. For a single
object download, you may use the \-o [\-\-output] <filename> option to object download, you may use the \-o [\-\-output] <filename> option to
redirect the output to a specific file or if "-" then just redirect to stdout or redirect the output to a specific file or if "-" then just redirect to stdout or
with \-\-no-download actually not to write anything to disk. with \-\-no-download actually not to write anything to disk.
The \-\-ignore-checksum is an option that turn off checksum validation.
You can specify optional headers with the repeatable cURL-like option You can specify optional headers with the repeatable cURL-like option
\-H [\-\-header]. For more details and options see swift download \-\-help. \-H [\-\-header]. For more details and options see swift download \-\-help.
.RE .RE

@ -186,7 +186,8 @@ Download
container, or a list of objects depending on the arguments given. For a container, or a list of objects depending on the arguments given. For a
single object download, you may use the ``-o <filename>`` or ``--output <filename>`` single object download, you may use the ``-o <filename>`` or ``--output <filename>``
option to redirect the output to a specific file or ``-`` to option to redirect the output to a specific file or ``-`` to
redirect to stdout. You can specify optional headers with the repeatable redirect to stdout. The ``--ignore-checksum`` is an option that turn off
checksum validation. You can specify optional headers with the repeatable
cURL-like option ``-H [--header <name:value>]``. cURL-like option ``-H [--header <name:value>]``.
Delete Delete

@ -207,6 +207,9 @@ Options
Affects uploads, and allows empty 'pseudofolder' objects to be created Affects uploads, and allows empty 'pseudofolder' objects to be created
when the source of an upload is ``None``. when the source of an upload is ``None``.
``checksum``: ``True``
Affects uploads and downloads, and means if check md5 for downloads/uploads.
``shuffle``: ``False`` ``shuffle``: ``False``
When downloading objects, the default behaviour of the CLI is to shuffle When downloading objects, the default behaviour of the CLI is to shuffle
lists of objects in order to spread the load on storage drives when multiple lists of objects in order to spread the load on storage drives when multiple

@ -336,7 +336,7 @@ class _SwiftReader(object):
errors on failures caused by either invalid md5sum or size of the errors on failures caused by either invalid md5sum or size of the
data read. data read.
""" """
def __init__(self, path, body, headers): def __init__(self, path, body, headers, checksum=True):
self._path = path self._path = path
self._body = body self._body = body
self._actual_read = 0 self._actual_read = 0
@ -345,7 +345,7 @@ class _SwiftReader(object):
self._expected_etag = headers.get('etag') self._expected_etag = headers.get('etag')
if ('x-object-manifest' not in headers if ('x-object-manifest' not in headers
and 'x-static-large-object' not in headers): and 'x-static-large-object' not in headers and checksum):
self._actual_md5 = md5() self._actual_md5 = md5()
if 'content-length' in headers: if 'content-length' in headers:
@ -980,6 +980,7 @@ class SwiftService(object):
'header': [], 'header': [],
'skip_identical': False, 'skip_identical': False,
'out_directory': None, 'out_directory': None,
'checksum': True,
'out_file': None, 'out_file': None,
'remove_prefix': False, 'remove_prefix': False,
'shuffle' : False 'shuffle' : False
@ -1135,7 +1136,8 @@ class SwiftService(object):
headers_receipt = time() headers_receipt = time()
obj_body = _SwiftReader(path, body, headers) obj_body = _SwiftReader(path, body, headers,
options.get('checksum', True))
no_file = options['no_download'] no_file = options['no_download']
if out_file == "-" and not no_file: if out_file == "-" and not no_file:

@ -204,7 +204,7 @@ def st_delete(parser, args, output_manager):
st_download_options = '''[--all] [--marker <marker>] [--prefix <prefix>] st_download_options = '''[--all] [--marker <marker>] [--prefix <prefix>]
[--output <out_file>] [--output-dir <out_directory>] [--output <out_file>] [--output-dir <out_directory>]
[--object-threads <threads>] [--object-threads <threads>] [--ignore-checksum]
[--container-threads <threads>] [--no-download] [--container-threads <threads>] [--no-download]
[--skip-identical] [--remove-prefix] [--skip-identical] [--remove-prefix]
[--header <header:value>] [--no-shuffle] [--header <header:value>] [--no-shuffle]
@ -251,6 +251,7 @@ Optional arguments:
Example --header "content-type:text/plain" Example --header "content-type:text/plain"
--skip-identical Skip downloading files that are identical on both --skip-identical Skip downloading files that are identical on both
sides. sides.
--ignore-checksum Turn off checksum validation for downloads.
--no-shuffle By default, when downloading a complete account or --no-shuffle By default, when downloading a complete account or
container, download order is randomised in order to container, download order is randomised in order to
reduce the load on individual drives when multiple reduce the load on individual drives when multiple
@ -308,6 +309,9 @@ def st_download(parser, args, output_manager):
'--skip-identical', action='store_true', dest='skip_identical', '--skip-identical', action='store_true', dest='skip_identical',
default=False, help='Skip downloading files that are identical on ' default=False, help='Skip downloading files that are identical on '
'both sides.') 'both sides.')
parser.add_argument(
'--ignore-checksum', action='store_false', dest='checksum',
default=True, help='Turn off checksum validation for downloads.')
parser.add_argument( parser.add_argument(
'--no-shuffle', action='store_false', dest='shuffle', '--no-shuffle', action='store_false', dest='shuffle',
default=True, help='By default, download order is randomised in order ' default=True, help='By default, download order is randomised in order '

@ -103,6 +103,15 @@ class TestSwiftReader(unittest.TestCase):
self.assertEqual(sr._expected_etag, None) self.assertEqual(sr._expected_etag, None)
self.assertEqual(sr._actual_md5, None) self.assertEqual(sr._actual_md5, None)
def test_create_with_ignore_checksum(self):
# md5 should not be initialized if checksum is False
sr = self.sr('path', 'body', {}, False)
self.assertEqual(sr._path, 'path')
self.assertEqual(sr._body, 'body')
self.assertEqual(sr._content_length, None)
self.assertEqual(sr._expected_etag, None)
self.assertEqual(sr._actual_md5, None)
def test_create_with_content_length(self): def test_create_with_content_length(self):
sr = self.sr('path', 'body', {'content-length': 5}) sr = self.sr('path', 'body', {'content-length': 5})

@ -367,6 +367,24 @@ class TestShell(unittest.TestCase):
mock_open.assert_called_with('object', 'wb') mock_open.assert_called_with('object', 'wb')
self.assertEqual([], makedirs.mock_calls) self.assertEqual([], makedirs.mock_calls)
# Test downloading without md5 checks
objcontent = six.BytesIO(b'objcontent')
connection.return_value.get_object.side_effect = [
({'content-type': 'text/plain',
'etag': '2cbbfe139a744d6abbe695e17f3c1991'},
objcontent)
]
with mock.patch(BUILTIN_OPEN) as mock_open, mock.patch(
'swiftclient.service._SwiftReader') as sr:
argv = ["", "download", "container", "object", "--ignore-check"]
swiftclient.shell.main(argv)
connection.return_value.get_object.assert_called_with(
'container', 'object', headers={}, resp_chunk_size=65536,
response_dict={})
mock_open.assert_called_with('object', 'wb')
sr.assert_called_once_with('object', mock.ANY, mock.ANY, False)
self.assertEqual([], makedirs.mock_calls)
# Test downloading single object to stdout # Test downloading single object to stdout
objcontent = six.BytesIO(b'objcontent') objcontent = six.BytesIO(b'objcontent')
connection.return_value.get_object.side_effect = [ connection.return_value.get_object.side_effect = [