summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/manpages/swift.13
-rw-r--r--doc/source/cli.rst4
-rw-r--r--swiftclient/service.py4
-rwxr-xr-xswiftclient/shell.py9
-rw-r--r--tests/unit/test_service.py46
5 files changed, 64 insertions, 2 deletions
diff --git a/doc/manpages/swift.1 b/doc/manpages/swift.1
index 1f288d6..d44a7d5 100644
--- a/doc/manpages/swift.1
+++ b/doc/manpages/swift.1
@@ -102,6 +102,9 @@ with \-\-no-download actually not to write anything to disk.
The \-\-ignore-checksum is an option that turns off checksum validation.
You can specify optional headers with the repeatable cURL-like option
\-H [\-\-header]. For more details and options see swift download \-\-help.
+The \-\-ignore\-mtime option ignores the x\-object\-meta\-mtime metadata entry
+on the object (if present) and instead creates the downloaded files with
+fresh atime and mtime values.
.RE
\fBdelete\fR [\fIcommand-options\fR] [\fIcontainer\fR] [\fIobject\fR] [\fIobject\fR] [...]
diff --git a/doc/source/cli.rst b/doc/source/cli.rst
index 8d80d1b..8df95aa 100644
--- a/doc/source/cli.rst
+++ b/doc/source/cli.rst
@@ -188,7 +188,9 @@ Download
option to redirect the output to a specific file or ``-`` to
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>]``. ``--ignore-mtime`` ignores the
+ ``x-object-meta-mtime`` metadata entry on the object (if present) and instead
+ creates the downloaded files with fresh atime and mtime values.
Delete
------
diff --git a/swiftclient/service.py b/swiftclient/service.py
index b9b843e..5a43bf2 100644
--- a/swiftclient/service.py
+++ b/swiftclient/service.py
@@ -203,6 +203,7 @@ _default_local_options = {
'shuffle': False,
'destination': None,
'fresh_metadata': False,
+ 'ignore_mtime': False,
}
POLICY = 'X-Storage-Policy'
@@ -1240,7 +1241,8 @@ class SwiftService(object):
bytes_read = obj_body.bytes_read()
if fp is not None:
fp.close()
- if 'x-object-meta-mtime' in headers and not no_file:
+ if ('x-object-meta-mtime' in headers and not no_file
+ and not options['ignore_mtime']):
try:
mtime = float(headers['x-object-meta-mtime'])
except ValueError:
diff --git a/swiftclient/shell.py b/swiftclient/shell.py
index 841ed6e..19a224a 100755
--- a/swiftclient/shell.py
+++ b/swiftclient/shell.py
@@ -272,6 +272,9 @@ Optional arguments:
script to multiple servers). Enable this option to
submit download jobs to the thread pool in the order
they are listed in the object store.
+ --ignore-mtime Ignore the 'X-Object-Meta-Mtime' header when
+ downloading an object. Instead, create atime and mtime
+ with fresh timestamps.
'''.strip("\n")
@@ -332,6 +335,12 @@ def st_download(parser, args, output_manager):
'nightly automated download script to multiple servers). Enable this '
'option to submit download jobs to the thread pool in the order they '
'are listed in the object store.')
+ parser.add_argument(
+ '--ignore-mtime', action='store_true', dest='ignore_mtime',
+ default=False, help='By default, the object-meta-mtime header is used '
+ 'to store the access and modified timestamp for the downloaded file. '
+ 'With this option, the header is ignored and the timestamps are '
+ 'created freshly.')
(options, args) = parse_args(parser, args)
args = args[1:]
if options['out_file'] == '-':
diff --git a/tests/unit/test_service.py b/tests/unit/test_service.py
index b759e6b..6490b3f 100644
--- a/tests/unit/test_service.py
+++ b/tests/unit/test_service.py
@@ -2051,6 +2051,52 @@ class TestServiceDownload(_TestServiceBase):
)
self.assertEqual(expected_r, actual_r)
+ def test_download_object_job_ignore_mtime(self):
+ mock_conn = self._get_mock_connection()
+ objcontent = six.BytesIO(b'objcontent')
+ mock_conn.get_object.side_effect = [
+ ({'content-type': 'text/plain',
+ 'etag': '2cbbfe139a744d6abbe695e17f3c1991',
+ 'x-object-meta-mtime': '1454113727.682512'},
+ objcontent)
+ ]
+ expected_r = self._get_expected({
+ 'success': True,
+ 'start_time': 1,
+ 'finish_time': 2,
+ 'headers_receipt': 3,
+ 'auth_end_time': 4,
+ 'read_length': len(b'objcontent'),
+ })
+
+ with mock.patch.object(builtins, 'open') as mock_open, \
+ mock.patch('swiftclient.service.utime') as mock_utime:
+ written_content = Mock()
+ mock_open.return_value = written_content
+ s = SwiftService()
+ _opts = self.opts.copy()
+ _opts['no_download'] = False
+ _opts['ignore_mtime'] = True
+ actual_r = s._download_object_job(
+ mock_conn, 'test_c', 'test_o', _opts)
+ actual_r = dict( # Need to override the times we got from the call
+ actual_r,
+ **{
+ 'start_time': 1,
+ 'finish_time': 2,
+ 'headers_receipt': 3
+ }
+ )
+ mock_open.assert_called_once_with('test_o', 'wb')
+ self.assertEqual([], mock_utime.mock_calls)
+ written_content.write.assert_called_once_with(b'objcontent')
+
+ mock_conn.get_object.assert_called_once_with(
+ 'test_c', 'test_o', resp_chunk_size=65536, headers={},
+ response_dict={}
+ )
+ self.assertEqual(expected_r, actual_r)
+
def test_download_object_job_exception(self):
mock_conn = self._get_mock_connection()
mock_conn.get_object = Mock(side_effect=self.exc)