Merge "Add transaction id to errors"
This commit is contained in:
commit
fa9718deee
@ -19,10 +19,13 @@ OpenStack Swift client library used internally
|
|||||||
import socket
|
import socket
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
from urllib3.exceptions import HTTPError as urllib_http_error
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from requests.exceptions import RequestException, SSLError
|
from requests.exceptions import RequestException, SSLError
|
||||||
import http.client as http_client
|
import http.client as http_client
|
||||||
|
from requests.structures import CaseInsensitiveDict
|
||||||
from urllib.parse import quote, unquote
|
from urllib.parse import quote, unquote
|
||||||
from urllib.parse import urljoin, urlparse, urlunparse
|
from urllib.parse import urljoin, urlparse, urlunparse
|
||||||
from time import sleep, time
|
from time import sleep, time
|
||||||
@ -287,7 +290,7 @@ class _RetryBody(_ObjectBody):
|
|||||||
try:
|
try:
|
||||||
buf = self.resp.read(length)
|
buf = self.resp.read(length)
|
||||||
self.bytes_read += len(buf)
|
self.bytes_read += len(buf)
|
||||||
except (socket.error, RequestException):
|
except (socket.error, urllib_http_error, RequestException):
|
||||||
if self.conn.attempts > self.conn.retries:
|
if self.conn.attempts > self.conn.retries:
|
||||||
raise
|
raise
|
||||||
if (not buf and self.bytes_read < self.expected_length and
|
if (not buf and self.bytes_read < self.expected_length and
|
||||||
@ -735,9 +738,9 @@ def get_auth(auth_url, user, key, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def resp_header_dict(resp):
|
def resp_header_dict(resp):
|
||||||
resp_headers = {}
|
resp_headers = CaseInsensitiveDict()
|
||||||
for header, value in resp.getheaders():
|
for header, value in resp.getheaders():
|
||||||
header = parse_header_string(header).lower()
|
header = parse_header_string(header)
|
||||||
resp_headers[header] = parse_header_string(value)
|
resp_headers[header] = parse_header_string(value)
|
||||||
return resp_headers
|
return resp_headers
|
||||||
|
|
||||||
@ -1926,6 +1929,7 @@ class Connection:
|
|||||||
is_not_range_request and resp_chunk_size and
|
is_not_range_request and resp_chunk_size and
|
||||||
self.attempts <= self.retries and
|
self.attempts <= self.retries and
|
||||||
rheaders.get('transfer-encoding') is None)
|
rheaders.get('transfer-encoding') is None)
|
||||||
|
|
||||||
if retry_is_possible:
|
if retry_is_possible:
|
||||||
body = _RetryBody(body.resp, self, container, obj,
|
body = _RetryBody(body.resp, self, container, obj,
|
||||||
resp_chunk_size=resp_chunk_size,
|
resp_chunk_size=resp_chunk_size,
|
||||||
|
@ -89,6 +89,10 @@ class OutputManager:
|
|||||||
msg = msg % fmt_args
|
msg = msg % fmt_args
|
||||||
self.error_print_pool.submit(self._print_error, msg)
|
self.error_print_pool.submit(self._print_error, msg)
|
||||||
|
|
||||||
|
def error_with_txn_id(self, swift_err):
|
||||||
|
self.error("{}\nFailed Transaction ID: {}".format(
|
||||||
|
swift_err.value, swift_err.transaction_id or 'unknown'))
|
||||||
|
|
||||||
def get_error_count(self):
|
def get_error_count(self):
|
||||||
return self.error_count
|
return self.error_count
|
||||||
|
|
||||||
|
@ -32,6 +32,9 @@ from time import time
|
|||||||
from threading import Thread
|
from threading import Thread
|
||||||
from queue import Queue
|
from queue import Queue
|
||||||
from queue import Empty as QueueEmpty
|
from queue import Empty as QueueEmpty
|
||||||
|
from requests.exceptions import RequestException
|
||||||
|
from socket import error as socket_error
|
||||||
|
from urllib3.exceptions import HTTPError as urllib_http_error
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
import json
|
import json
|
||||||
@ -68,12 +71,16 @@ class ResultsIterator:
|
|||||||
|
|
||||||
class SwiftError(Exception):
|
class SwiftError(Exception):
|
||||||
def __init__(self, value, container=None, obj=None,
|
def __init__(self, value, container=None, obj=None,
|
||||||
segment=None, exc=None):
|
segment=None, exc=None, transaction_id=None):
|
||||||
self.value = value
|
self.value = value
|
||||||
self.container = container
|
self.container = container
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
self.segment = segment
|
self.segment = segment
|
||||||
self.exception = exc
|
self.exception = exc
|
||||||
|
if transaction_id is None:
|
||||||
|
self.transaction_id = getattr(exc, 'transaction_id', None)
|
||||||
|
else:
|
||||||
|
self.transaction_id = transaction_id
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
value = repr(self.value)
|
value = repr(self.value)
|
||||||
@ -459,7 +466,9 @@ class _SwiftReader:
|
|||||||
try:
|
try:
|
||||||
self._content_length = int(headers.get('content-length'))
|
self._content_length = int(headers.get('content-length'))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise SwiftError('content-length header must be an integer')
|
raise SwiftError(
|
||||||
|
'content-length header must be an integer',
|
||||||
|
transaction_id=self._txn_id)
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
for chunk in self._body:
|
for chunk in self._body:
|
||||||
@ -1306,9 +1315,15 @@ class SwiftService:
|
|||||||
else:
|
else:
|
||||||
pseudodir = True
|
pseudodir = True
|
||||||
|
|
||||||
for chunk in obj_body:
|
try:
|
||||||
if fp is not None:
|
for chunk in obj_body:
|
||||||
fp.write(chunk)
|
if fp is not None:
|
||||||
|
fp.write(chunk)
|
||||||
|
except (socket_error,
|
||||||
|
urllib_http_error,
|
||||||
|
RequestException) as err:
|
||||||
|
raise ClientException(
|
||||||
|
str(err), http_response_headers=headers)
|
||||||
|
|
||||||
finish_time = time()
|
finish_time = time()
|
||||||
|
|
||||||
|
@ -227,7 +227,7 @@ def st_delete(parser, args, output_manager, return_parser=False):
|
|||||||
output_manager.error('Error Deleting: {0}: {1}'
|
output_manager.error('Error Deleting: {0}: {1}'
|
||||||
.format(p, r['error']))
|
.format(p, r['error']))
|
||||||
except SwiftError as err:
|
except SwiftError as err:
|
||||||
output_manager.error(err.value)
|
output_manager.error_with_txn_id(err)
|
||||||
|
|
||||||
|
|
||||||
st_download_options = '''[--all] [--marker <marker>] [--prefix <prefix>]
|
st_download_options = '''[--all] [--marker <marker>] [--prefix <prefix>]
|
||||||
@ -484,11 +484,13 @@ def st_download(parser, args, output_manager, return_parser=False):
|
|||||||
"Object '%s/%s' not found", container, obj)
|
"Object '%s/%s' not found", container, obj)
|
||||||
continue
|
continue
|
||||||
output_manager.error(
|
output_manager.error(
|
||||||
"Error downloading object '%s/%s': %s",
|
"Error downloading object '%s/%s': %s\n"
|
||||||
container, obj, error)
|
"Failed Transaction ID: %s",
|
||||||
|
container, obj, error,
|
||||||
|
getattr(error, 'transaction_id', 'unknown'))
|
||||||
|
|
||||||
except SwiftError as e:
|
except SwiftError as e:
|
||||||
output_manager.error(e.value)
|
output_manager.error_with_txn_id(e)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
output_manager.error(e)
|
output_manager.error(e)
|
||||||
|
|
||||||
@ -670,7 +672,7 @@ def st_list(parser, args, output_manager, return_parser=False):
|
|||||||
prt_bytes(totals['bytes'], human))
|
prt_bytes(totals['bytes'], human))
|
||||||
|
|
||||||
except SwiftError as e:
|
except SwiftError as e:
|
||||||
output_manager.error(e.value)
|
output_manager.error_with_txn_id(e)
|
||||||
|
|
||||||
|
|
||||||
st_stat_options = '''[--lh] [--header <header:value>]
|
st_stat_options = '''[--lh] [--header <header:value>]
|
||||||
@ -761,7 +763,7 @@ def st_stat(parser, args, output_manager, return_parser=False):
|
|||||||
st_stat_options, st_stat_help)
|
st_stat_options, st_stat_help)
|
||||||
|
|
||||||
except SwiftError as e:
|
except SwiftError as e:
|
||||||
output_manager.error(e.value)
|
output_manager.error_with_txn_id(e)
|
||||||
|
|
||||||
|
|
||||||
st_post_options = '''[--read-acl <acl>] [--write-acl <acl>] [--sync-to <sync-to>]
|
st_post_options = '''[--read-acl <acl>] [--write-acl <acl>] [--sync-to <sync-to>]
|
||||||
@ -867,7 +869,7 @@ def st_post(parser, args, output_manager, return_parser=False):
|
|||||||
raise result["error"]
|
raise result["error"]
|
||||||
|
|
||||||
except SwiftError as e:
|
except SwiftError as e:
|
||||||
output_manager.error(e.value)
|
output_manager.error_with_txn_id(e)
|
||||||
|
|
||||||
|
|
||||||
st_copy_options = '''[--destination </container/object>] [--fresh-metadata]
|
st_copy_options = '''[--destination </container/object>] [--fresh-metadata]
|
||||||
@ -972,7 +974,7 @@ def st_copy(parser, args, output_manager, return_parser=False):
|
|||||||
return
|
return
|
||||||
|
|
||||||
except SwiftError as e:
|
except SwiftError as e:
|
||||||
output_manager.error(e.value)
|
output_manager.error_with_txn_id(e)
|
||||||
|
|
||||||
|
|
||||||
st_upload_options = '''[--changed] [--skip-identical] [--segment-size <size>]
|
st_upload_options = '''[--changed] [--skip-identical] [--segment-size <size>]
|
||||||
@ -1270,7 +1272,7 @@ def st_upload(parser, args, output_manager, return_parser=False):
|
|||||||
"to chunk the object")
|
"to chunk the object")
|
||||||
|
|
||||||
except SwiftError as e:
|
except SwiftError as e:
|
||||||
output_manager.error(e.value)
|
output_manager.error_with_txn_id(e)
|
||||||
|
|
||||||
|
|
||||||
st_capabilities_options = '''[--json] [<proxy_url>]
|
st_capabilities_options = '''[--json] [<proxy_url>]
|
||||||
@ -1332,7 +1334,7 @@ def st_capabilities(parser, args, output_manager, return_parser=False):
|
|||||||
del capabilities['swift']
|
del capabilities['swift']
|
||||||
_print_compo_cap('Additional middleware', capabilities)
|
_print_compo_cap('Additional middleware', capabilities)
|
||||||
except SwiftError as e:
|
except SwiftError as e:
|
||||||
output_manager.error(e.value)
|
output_manager.error_with_txn_id(e)
|
||||||
|
|
||||||
|
|
||||||
st_info = st_capabilities
|
st_info = st_capabilities
|
||||||
|
@ -27,6 +27,7 @@ from unittest import mock
|
|||||||
from concurrent.futures import Future
|
from concurrent.futures import Future
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from queue import Queue, Empty as QueueEmptyError
|
from queue import Queue, Empty as QueueEmptyError
|
||||||
|
from requests.structures import CaseInsensitiveDict
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
import swiftclient
|
import swiftclient
|
||||||
@ -167,8 +168,10 @@ class TestSwiftReader(unittest.TestCase):
|
|||||||
self.assertIsNone(sr._actual_md5)
|
self.assertIsNone(sr._actual_md5)
|
||||||
|
|
||||||
# Check Contentlength raises error if it isn't an integer
|
# Check Contentlength raises error if it isn't an integer
|
||||||
self.assertRaises(SwiftError, self.sr, 'path', 'body',
|
with self.assertRaises(SwiftError) as cm:
|
||||||
{'content-length': 'notanint'})
|
self.sr('path', 'body', {'content-length': 'notanint'})
|
||||||
|
self.assertEqual("'content-length header must be an integer'",
|
||||||
|
str(cm.exception))
|
||||||
|
|
||||||
def test_iterator_usage(self):
|
def test_iterator_usage(self):
|
||||||
def _consume(sr):
|
def _consume(sr):
|
||||||
@ -653,6 +656,7 @@ class TestSwiftError(unittest.TestCase):
|
|||||||
self.assertIsNone(se.exception)
|
self.assertIsNone(se.exception)
|
||||||
|
|
||||||
self.assertEqual(str(se), '5')
|
self.assertEqual(str(se), '5')
|
||||||
|
self.assertNotIn(str(se), 'Transaction ID')
|
||||||
|
|
||||||
def test_swifterror_creation(self):
|
def test_swifterror_creation(self):
|
||||||
test_exc = Exception('test exc')
|
test_exc = Exception('test exc')
|
||||||
@ -665,6 +669,25 @@ class TestSwiftError(unittest.TestCase):
|
|||||||
self.assertEqual(se.exception, test_exc)
|
self.assertEqual(se.exception, test_exc)
|
||||||
|
|
||||||
self.assertEqual(str(se), '5 container:con object:obj segment:seg')
|
self.assertEqual(str(se), '5 container:con object:obj segment:seg')
|
||||||
|
self.assertNotIn(str(se), 'Transaction ID')
|
||||||
|
|
||||||
|
def test_swifterror_clientexception_creation(self):
|
||||||
|
test_exc = ClientException(
|
||||||
|
Exception('test exc'),
|
||||||
|
http_response_headers=CaseInsensitiveDict({
|
||||||
|
'x-trans-id': 'someTransId'})
|
||||||
|
)
|
||||||
|
se = SwiftError(5, 'con', 'obj', 'seg', test_exc)
|
||||||
|
|
||||||
|
self.assertEqual(se.value, 5)
|
||||||
|
self.assertEqual(se.container, 'con')
|
||||||
|
self.assertEqual(se.obj, 'obj')
|
||||||
|
self.assertEqual(se.segment, 'seg')
|
||||||
|
self.assertEqual(se.exception, test_exc)
|
||||||
|
|
||||||
|
self.assertEqual('someTransId', se.transaction_id)
|
||||||
|
self.assertNotIn('someTransId', str(se))
|
||||||
|
self.assertIn('5 container:con object:obj segment:seg', str(se))
|
||||||
|
|
||||||
|
|
||||||
class TestServiceUtils(unittest.TestCase):
|
class TestServiceUtils(unittest.TestCase):
|
||||||
|
@ -15,17 +15,21 @@
|
|||||||
|
|
||||||
import io
|
import io
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import socket
|
||||||
from genericpath import getmtime
|
from genericpath import getmtime
|
||||||
import getpass
|
import getpass
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from requests.structures import CaseInsensitiveDict
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
import textwrap
|
import textwrap
|
||||||
from time import localtime, mktime, strftime, strptime
|
from time import localtime, mktime, strftime, strptime
|
||||||
|
from requests.exceptions import RequestException
|
||||||
|
from urllib3.exceptions import HTTPError
|
||||||
|
|
||||||
import swiftclient
|
import swiftclient
|
||||||
from swiftclient.service import SwiftError
|
from swiftclient.service import SwiftError
|
||||||
@ -235,6 +239,28 @@ class TestShell(unittest.TestCase):
|
|||||||
' Sync To: other\n'
|
' Sync To: other\n'
|
||||||
' Sync Key: secret\n')
|
' Sync Key: secret\n')
|
||||||
|
|
||||||
|
@mock.patch('swiftclient.service.Connection')
|
||||||
|
def test_stat_container_not_found(self, connection):
|
||||||
|
connection.return_value.head_container.side_effect = \
|
||||||
|
swiftclient.ClientException(
|
||||||
|
'test',
|
||||||
|
http_status=404,
|
||||||
|
http_response_headers=CaseInsensitiveDict({
|
||||||
|
'x-trans-id': 'someTransId'})
|
||||||
|
)
|
||||||
|
argv = ["", "stat", "container"]
|
||||||
|
|
||||||
|
with CaptureOutput() as output:
|
||||||
|
with self.assertRaises(SystemExit):
|
||||||
|
swiftclient.shell.main(argv)
|
||||||
|
connection.return_value.head_container.assert_called_with(
|
||||||
|
'container', headers={}, resp_chunk_size=65536,
|
||||||
|
response_dict={})
|
||||||
|
|
||||||
|
self.assertIn('Container \'container\' not found\n'
|
||||||
|
'Failed Transaction ID: someTransId',
|
||||||
|
output.err)
|
||||||
|
|
||||||
@mock.patch('swiftclient.service.Connection')
|
@mock.patch('swiftclient.service.Connection')
|
||||||
def test_stat_container_with_headers(self, connection):
|
def test_stat_container_with_headers(self, connection):
|
||||||
return_headers = {
|
return_headers = {
|
||||||
@ -313,6 +339,27 @@ class TestShell(unittest.TestCase):
|
|||||||
' ETag: md5\n'
|
' ETag: md5\n'
|
||||||
' Manifest: manifest\n')
|
' Manifest: manifest\n')
|
||||||
|
|
||||||
|
@mock.patch('swiftclient.service.Connection')
|
||||||
|
def test_stat_object_not_found(self, connection):
|
||||||
|
connection.return_value.head_object.side_effect = \
|
||||||
|
swiftclient.ClientException(
|
||||||
|
'test', http_status=404,
|
||||||
|
http_response_headers=CaseInsensitiveDict({
|
||||||
|
'x-trans-id': 'someTransId'})
|
||||||
|
)
|
||||||
|
argv = ["", "stat", "container", "object"]
|
||||||
|
|
||||||
|
with CaptureOutput() as output:
|
||||||
|
with self.assertRaises(SystemExit):
|
||||||
|
swiftclient.shell.main(argv)
|
||||||
|
connection.return_value.head_object.assert_called_with(
|
||||||
|
'container', 'object', headers={}, resp_chunk_size=65536,
|
||||||
|
response_dict={})
|
||||||
|
|
||||||
|
self.assertIn('test: 404\n'
|
||||||
|
'Failed Transaction ID: someTransId',
|
||||||
|
output.err)
|
||||||
|
|
||||||
@mock.patch('swiftclient.service.Connection')
|
@mock.patch('swiftclient.service.Connection')
|
||||||
def test_stat_object_with_headers(self, connection):
|
def test_stat_object_with_headers(self, connection):
|
||||||
return_headers = {
|
return_headers = {
|
||||||
@ -735,6 +782,117 @@ class TestShell(unittest.TestCase):
|
|||||||
swiftclient.shell.main(argv)
|
swiftclient.shell.main(argv)
|
||||||
self.assertEqual('objcontent', output.out)
|
self.assertEqual('objcontent', output.out)
|
||||||
|
|
||||||
|
def _do_test_download_clientexception(self, exc):
|
||||||
|
retry_calls = []
|
||||||
|
|
||||||
|
def _fake_retry(conn, *args, **kwargs):
|
||||||
|
retry_calls.append((args, kwargs))
|
||||||
|
conn.attempts += 1
|
||||||
|
|
||||||
|
body = mock.MagicMock()
|
||||||
|
body.resp.read.side_effect = RequestException('test_exc')
|
||||||
|
return (CaseInsensitiveDict({
|
||||||
|
'content-type': 'text/plain',
|
||||||
|
'etag': '2cbbfe139a744d6abbe695e17f3c1991',
|
||||||
|
'x-trans-id': 'someTransId'}),
|
||||||
|
body)
|
||||||
|
|
||||||
|
argv = ["", "download", "container", "object", "--retries", "1"]
|
||||||
|
with CaptureOutput() as output:
|
||||||
|
with mock.patch(BUILTIN_OPEN) as mock_open:
|
||||||
|
with mock.patch("swiftclient.service.Connection._retry",
|
||||||
|
_fake_retry):
|
||||||
|
with self.assertRaises(SystemExit):
|
||||||
|
swiftclient.shell.main(argv)
|
||||||
|
mock_open.assert_called_with('object', 'wb', 65536)
|
||||||
|
self.assertEqual([
|
||||||
|
((None, swiftclient.client.get_object, 'container', 'object'),
|
||||||
|
{'headers': {},
|
||||||
|
'query_string': None,
|
||||||
|
'resp_chunk_size': 65536,
|
||||||
|
'response_dict': {}}),
|
||||||
|
((None, swiftclient.client.get_object, 'container', 'object'),
|
||||||
|
{'attempts': 1,
|
||||||
|
'headers': {'If-Match': mock.ANY, 'Range': 'bytes=0-'},
|
||||||
|
'query_string': None,
|
||||||
|
'resp_chunk_size': 65536,
|
||||||
|
'response_dict': {}})],
|
||||||
|
retry_calls)
|
||||||
|
self.assertIn('Error downloading object \'container/object\': '
|
||||||
|
'test_exc',
|
||||||
|
str(output.err))
|
||||||
|
self.assertIn('someTransId', str(output.err))
|
||||||
|
|
||||||
|
def test_download_request_exception(self):
|
||||||
|
self._do_test_download_clientexception(RequestException('text_exc'))
|
||||||
|
|
||||||
|
def test_download_socket_error(self):
|
||||||
|
self._do_test_download_clientexception(socket.error())
|
||||||
|
|
||||||
|
def test_download_http_error(self):
|
||||||
|
self._do_test_download_clientexception(HTTPError)
|
||||||
|
|
||||||
|
def test_download_request_exception_retries_0(self):
|
||||||
|
retry_calls = []
|
||||||
|
|
||||||
|
def _fake_retry(conn, *args, **kwargs):
|
||||||
|
retry_calls.append((args, kwargs))
|
||||||
|
conn.attempts += 1
|
||||||
|
|
||||||
|
body = mock.MagicMock()
|
||||||
|
body.__iter__.side_effect = RequestException('test_exc')
|
||||||
|
return (CaseInsensitiveDict({
|
||||||
|
'content-type': 'text/plain',
|
||||||
|
'etag': '2cbbfe139a744d6abbe695e17f3c1991',
|
||||||
|
'x-trans-id': 'someTransId'}),
|
||||||
|
body)
|
||||||
|
|
||||||
|
argv = ["", "download", "container", "object", "--retries", "0"]
|
||||||
|
with CaptureOutput() as output:
|
||||||
|
with mock.patch(BUILTIN_OPEN) as mock_open:
|
||||||
|
with mock.patch("swiftclient.service.Connection._retry",
|
||||||
|
_fake_retry):
|
||||||
|
with self.assertRaises(SystemExit):
|
||||||
|
swiftclient.shell.main(argv)
|
||||||
|
mock_open.assert_called_with('object', 'wb', 65536)
|
||||||
|
self.assertEqual([
|
||||||
|
((None, swiftclient.client.get_object, 'container', 'object'),
|
||||||
|
{'headers': {},
|
||||||
|
'query_string': None,
|
||||||
|
'resp_chunk_size': 65536,
|
||||||
|
'response_dict': {}}), ],
|
||||||
|
retry_calls)
|
||||||
|
self.assertIn('Error downloading object \'container/object\': '
|
||||||
|
'test_exc',
|
||||||
|
str(output.err))
|
||||||
|
self.assertIn('someTransId', str(output.err))
|
||||||
|
|
||||||
|
@mock.patch('swiftclient.service.Connection')
|
||||||
|
def test_download_bad_content_length(self, connection):
|
||||||
|
objcontent = io.BytesIO(b'objcontent')
|
||||||
|
connection.return_value.get_object.side_effect = [
|
||||||
|
(CaseInsensitiveDict({
|
||||||
|
'content-type': 'text/plain',
|
||||||
|
'content-length': 'BAD',
|
||||||
|
'etag': '2cbbfe139a744d6abbe695e17f3c1991',
|
||||||
|
'x-trans-id': 'someTransId'}),
|
||||||
|
objcontent)
|
||||||
|
]
|
||||||
|
with CaptureOutput() as output:
|
||||||
|
with self.assertRaises(SystemExit):
|
||||||
|
with mock.patch(BUILTIN_OPEN) as mock_open:
|
||||||
|
argv = ["", "download", "container", "object"]
|
||||||
|
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', 65536)
|
||||||
|
|
||||||
|
self.assertIn("Error downloading object \'container/object\': "
|
||||||
|
"'content-length header must be an integer'"
|
||||||
|
"\nFailed Transaction ID: someTransId",
|
||||||
|
str(output.err))
|
||||||
|
|
||||||
@mock.patch('swiftclient.service.shuffle')
|
@mock.patch('swiftclient.service.shuffle')
|
||||||
@mock.patch('swiftclient.service.Connection')
|
@mock.patch('swiftclient.service.Connection')
|
||||||
def test_download_shuffle(self, connection, mock_shuffle):
|
def test_download_shuffle(self, connection, mock_shuffle):
|
||||||
@ -1930,7 +2088,9 @@ class TestShell(unittest.TestCase):
|
|||||||
with self.assertRaises(SystemExit):
|
with self.assertRaises(SystemExit):
|
||||||
swiftclient.shell.main(argv)
|
swiftclient.shell.main(argv)
|
||||||
|
|
||||||
self.assertEqual(output.err, 'Account not found\n')
|
self.assertEqual(
|
||||||
|
output.err,
|
||||||
|
'Account not found\nFailed Transaction ID: unknown\n')
|
||||||
|
|
||||||
@mock.patch('swiftclient.service.Connection')
|
@mock.patch('swiftclient.service.Connection')
|
||||||
def test_post_container(self, connection):
|
def test_post_container(self, connection):
|
||||||
@ -2122,7 +2282,8 @@ class TestShell(unittest.TestCase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
output.err,
|
output.err,
|
||||||
'Combination of multiple objects and destination '
|
'Combination of multiple objects and destination '
|
||||||
'including object is invalid\n')
|
'including object is invalid\n'
|
||||||
|
'Failed Transaction ID: unknown\n')
|
||||||
|
|
||||||
@mock.patch('swiftclient.service.Connection')
|
@mock.patch('swiftclient.service.Connection')
|
||||||
def test_copy_object_bad_auth(self, connection):
|
def test_copy_object_bad_auth(self, connection):
|
||||||
|
@ -1118,6 +1118,14 @@ class TestGetObject(MockHttpTest):
|
|||||||
self.assertEqual('%ff', headers.get('x-non-utf-8-header', ''))
|
self.assertEqual('%ff', headers.get('x-non-utf-8-header', ''))
|
||||||
self.assertEqual('%FF', headers.get('x-binary-header', ''))
|
self.assertEqual('%FF', headers.get('x-binary-header', ''))
|
||||||
|
|
||||||
|
self.assertEqual('t\xe9st', headers.get('X-Utf-8-Header', ''))
|
||||||
|
self.assertEqual('%ff', headers.get('X-Non-Utf-8-Header', ''))
|
||||||
|
self.assertEqual('%FF', headers.get('X-Binary-Header', ''))
|
||||||
|
|
||||||
|
self.assertEqual('t\xe9st', headers.get('X-UTF-8-HEADER', ''))
|
||||||
|
self.assertEqual('%ff', headers.get('X-NON-UTF-8-HEADER', ''))
|
||||||
|
self.assertEqual('%FF', headers.get('X-BINARY-HEADER', ''))
|
||||||
|
|
||||||
def test_chunk_size_read_method(self):
|
def test_chunk_size_read_method(self):
|
||||||
conn = c.Connection('http://auth.url/', 'some_user', 'some_key')
|
conn = c.Connection('http://auth.url/', 'some_user', 'some_key')
|
||||||
with mock.patch('swiftclient.client.get_auth_1_0') as mock_get_auth:
|
with mock.patch('swiftclient.client.get_auth_1_0') as mock_get_auth:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user