Replace old httpclient with requests
This review implements blueprint python-request and replaces the old
http client implementation in favor of a new one based on
python-requests.
Major changes:
* raw_request and json_request removed since everything is now being
handled by the same method "_request"
* New methods that match HTTP's methods were added:
- get
- put
- post
- head
- patch
- delete
* Content-Type is now being "inferred" based on the data being sent:
- if it is file-like object it chunks the request
- if it is a python type not instance of basestring then it'll try
to serialize it to json
- Every other case will keep the incoming content-type and will send
the data as is.
* Glanceclient's HTTPSConnection implementation will be used if
no-compression flag is set to True.
Co-Author: Flavio Percoco<flaper87@gmail.com>
Change-Id: I09f70eee3e2777f52ce040296015d41649c2586a
This commit is contained in:
+4
-6
@@ -12,18 +12,16 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import collections
|
||||
import mock
|
||||
import testtools
|
||||
|
||||
from glanceclient import exc
|
||||
|
||||
|
||||
FakeResponse = collections.namedtuple('HTTPResponse', ['status'])
|
||||
|
||||
|
||||
class TestHTTPExceptions(testtools.TestCase):
|
||||
def test_from_response(self):
|
||||
"""exc.from_response should return instance of an HTTP exception."""
|
||||
out = exc.from_response(FakeResponse(400))
|
||||
mock_resp = mock.Mock()
|
||||
mock_resp.status_code = 400
|
||||
out = exc.from_response(mock_resp)
|
||||
self.assertIsInstance(out, exc.HTTPBadRequest)
|
||||
|
||||
+103
-302
@@ -12,21 +12,18 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import json
|
||||
|
||||
import errno
|
||||
import socket
|
||||
|
||||
import mock
|
||||
from mox3 import mox
|
||||
import requests
|
||||
import six
|
||||
from six.moves import http_client
|
||||
from six.moves.urllib import parse
|
||||
import tempfile
|
||||
import testtools
|
||||
import types
|
||||
|
||||
import glanceclient
|
||||
from glanceclient.common import http
|
||||
from glanceclient.common import utils as client_utils
|
||||
from glanceclient.common import https
|
||||
from glanceclient import exc
|
||||
from tests import utils
|
||||
|
||||
@@ -36,8 +33,7 @@ class TestClient(testtools.TestCase):
|
||||
def setUp(self):
|
||||
super(TestClient, self).setUp()
|
||||
self.mock = mox.Mox()
|
||||
self.mock.StubOutWithMock(http_client.HTTPConnection, 'request')
|
||||
self.mock.StubOutWithMock(http_client.HTTPConnection, 'getresponse')
|
||||
self.mock.StubOutWithMock(requests.Session, 'request')
|
||||
|
||||
self.endpoint = 'http://example.com:9292'
|
||||
self.client = http.HTTPClient(self.endpoint, token=u'abc123')
|
||||
@@ -85,14 +81,16 @@ class TestClient(testtools.TestCase):
|
||||
And the error should list the host and port that refused the
|
||||
connection
|
||||
"""
|
||||
http_client.HTTPConnection.request(
|
||||
requests.Session.request(
|
||||
mox.IgnoreArg(),
|
||||
mox.IgnoreArg(),
|
||||
data=mox.IgnoreArg(),
|
||||
headers=mox.IgnoreArg(),
|
||||
).AndRaise(socket.error())
|
||||
stream=mox.IgnoreArg(),
|
||||
).AndRaise(requests.exceptions.ConnectionError())
|
||||
self.mock.ReplayAll()
|
||||
try:
|
||||
self.client.json_request('GET', '/v1/images/detail?limit=20')
|
||||
self.client.get('/v1/images/detail?limit=20')
|
||||
#NOTE(alaski) We expect exc.CommunicationError to be raised
|
||||
# so we should never reach this point. try/except is used here
|
||||
# rather than assertRaises() so that we can check the body of
|
||||
@@ -103,47 +101,23 @@ class TestClient(testtools.TestCase):
|
||||
(comm_err.message, self.endpoint))
|
||||
self.assertTrue(self.endpoint in comm_err.message, fail_msg)
|
||||
|
||||
def test_request_redirected(self):
|
||||
resp = utils.FakeResponse({'location': 'http://www.example.com'},
|
||||
status=302, body=six.BytesIO())
|
||||
http_client.HTTPConnection.request(
|
||||
mox.IgnoreArg(),
|
||||
mox.IgnoreArg(),
|
||||
headers=mox.IgnoreArg(),
|
||||
)
|
||||
http_client.HTTPConnection.getresponse().AndReturn(resp)
|
||||
|
||||
# The second request should be to the redirected location
|
||||
expected_response = b'Ok'
|
||||
resp2 = utils.FakeResponse({}, six.BytesIO(expected_response))
|
||||
http_client.HTTPConnection.request(
|
||||
'GET',
|
||||
'http://www.example.com',
|
||||
headers=mox.IgnoreArg(),
|
||||
)
|
||||
http_client.HTTPConnection.getresponse().AndReturn(resp2)
|
||||
|
||||
self.mock.ReplayAll()
|
||||
|
||||
self.client.json_request('GET', '/v1/images/detail')
|
||||
|
||||
def test_http_encoding(self):
|
||||
http_client.HTTPConnection.request(
|
||||
mox.IgnoreArg(),
|
||||
mox.IgnoreArg(),
|
||||
headers=mox.IgnoreArg())
|
||||
|
||||
# Lets fake the response
|
||||
# returned by httplib
|
||||
expected_response = b'Ok'
|
||||
fake = utils.FakeResponse({}, six.BytesIO(expected_response))
|
||||
http_client.HTTPConnection.getresponse().AndReturn(fake)
|
||||
# returned by requests
|
||||
response = 'Ok'
|
||||
headers = {"Content-Type": "text/plain"}
|
||||
fake = utils.FakeResponse(headers, six.StringIO(response))
|
||||
requests.Session.request(
|
||||
mox.IgnoreArg(),
|
||||
mox.IgnoreArg(),
|
||||
data=mox.IgnoreArg(),
|
||||
stream=mox.IgnoreArg(),
|
||||
headers=mox.IgnoreArg()).AndReturn(fake)
|
||||
self.mock.ReplayAll()
|
||||
|
||||
headers = {"test": u'ni\xf1o'}
|
||||
resp, body = self.client.raw_request('GET', '/v1/images/detail',
|
||||
headers=headers)
|
||||
self.assertEqual(fake, resp)
|
||||
resp, body = self.client.get('/v1/images/detail', headers=headers)
|
||||
self.assertEqual(resp, fake)
|
||||
|
||||
def test_headers_encoding(self):
|
||||
value = u'ni\xf1o'
|
||||
@@ -156,153 +130,19 @@ class TestClient(testtools.TestCase):
|
||||
|
||||
def test_raw_request(self):
|
||||
" Verify the path being used for HTTP requests reflects accurately. "
|
||||
|
||||
def check_request(method, path, **kwargs):
|
||||
self.assertEqual('GET', method)
|
||||
# NOTE(kmcdonald): See bug #1179984 for more details.
|
||||
self.assertEqual('/v1/images/detail', path)
|
||||
|
||||
http_client.HTTPConnection.request(
|
||||
headers = {"Content-Type": "text/plain"}
|
||||
response = 'Ok'
|
||||
fake = utils.FakeResponse({}, six.StringIO(response))
|
||||
requests.Session.request(
|
||||
mox.IgnoreArg(),
|
||||
mox.IgnoreArg(),
|
||||
headers=mox.IgnoreArg()).WithSideEffects(check_request)
|
||||
|
||||
# fake the response returned by httplib
|
||||
fake = utils.FakeResponse({}, six.BytesIO(b'Ok'))
|
||||
http_client.HTTPConnection.getresponse().AndReturn(fake)
|
||||
data=mox.IgnoreArg(),
|
||||
stream=mox.IgnoreArg(),
|
||||
headers=mox.IgnoreArg()).AndReturn(fake)
|
||||
self.mock.ReplayAll()
|
||||
|
||||
resp, body = self.client.raw_request('GET', '/v1/images/detail')
|
||||
self.assertEqual(fake, resp)
|
||||
|
||||
def test_customized_path_raw_request(self):
|
||||
"""
|
||||
Verify the customized path being used for HTTP requests
|
||||
reflects accurately
|
||||
"""
|
||||
|
||||
def check_request(method, path, **kwargs):
|
||||
self.assertEqual('GET', method)
|
||||
self.assertEqual('/customized-path/v1/images/detail', path)
|
||||
|
||||
# NOTE(yuyangbj): see bug 1230032 to get more info
|
||||
endpoint = 'http://example.com:9292/customized-path'
|
||||
client = http.HTTPClient(endpoint, token=u'abc123')
|
||||
self.assertEqual('/customized-path', client.endpoint_path)
|
||||
|
||||
http_client.HTTPConnection.request(
|
||||
mox.IgnoreArg(),
|
||||
mox.IgnoreArg(),
|
||||
headers=mox.IgnoreArg()).WithSideEffects(check_request)
|
||||
|
||||
# fake the response returned by httplib
|
||||
fake = utils.FakeResponse({}, six.BytesIO(b'Ok'))
|
||||
http_client.HTTPConnection.getresponse().AndReturn(fake)
|
||||
self.mock.ReplayAll()
|
||||
|
||||
resp, body = client.raw_request('GET', '/v1/images/detail')
|
||||
self.assertEqual(fake, resp)
|
||||
|
||||
def test_raw_request_no_content_length(self):
|
||||
with tempfile.NamedTemporaryFile() as test_file:
|
||||
test_file.write(b'abcd')
|
||||
test_file.seek(0)
|
||||
data_length = 4
|
||||
self.assertEqual(data_length,
|
||||
client_utils.get_file_size(test_file))
|
||||
|
||||
exp_resp = {'body': test_file}
|
||||
exp_resp['headers'] = {'Content-Length': str(data_length),
|
||||
'Content-Type': 'application/octet-stream'}
|
||||
|
||||
def mock_request(url, method, **kwargs):
|
||||
return kwargs
|
||||
|
||||
rq_kwargs = {'body': test_file, 'content_length': None}
|
||||
|
||||
with mock.patch.object(self.client, '_http_request') as mock_rq:
|
||||
mock_rq.side_effect = mock_request
|
||||
resp = self.client.raw_request('PUT', '/v1/images/detail',
|
||||
**rq_kwargs)
|
||||
|
||||
rq_kwargs.pop('content_length')
|
||||
headers = {'Content-Length': str(data_length),
|
||||
'Content-Type': 'application/octet-stream'}
|
||||
rq_kwargs['headers'] = headers
|
||||
|
||||
mock_rq.assert_called_once_with('/v1/images/detail', 'PUT',
|
||||
**rq_kwargs)
|
||||
|
||||
self.assertEqual(exp_resp, resp)
|
||||
|
||||
def test_raw_request_w_content_length(self):
|
||||
with tempfile.NamedTemporaryFile() as test_file:
|
||||
test_file.write(b'abcd')
|
||||
test_file.seek(0)
|
||||
data_length = 4
|
||||
self.assertEqual(data_length,
|
||||
client_utils.get_file_size(test_file))
|
||||
|
||||
exp_resp = {'body': test_file}
|
||||
# NOTE: we expect the actual file size to be overridden by the
|
||||
# supplied content length.
|
||||
exp_resp['headers'] = {'Content-Length': '4',
|
||||
'Content-Type': 'application/octet-stream'}
|
||||
|
||||
def mock_request(url, method, **kwargs):
|
||||
return kwargs
|
||||
|
||||
rq_kwargs = {'body': test_file, 'content_length': data_length}
|
||||
|
||||
with mock.patch.object(self.client, '_http_request') as mock_rq:
|
||||
mock_rq.side_effect = mock_request
|
||||
resp = self.client.raw_request('PUT', '/v1/images/detail',
|
||||
**rq_kwargs)
|
||||
|
||||
rq_kwargs.pop('content_length')
|
||||
headers = {'Content-Length': str(data_length),
|
||||
'Content-Type': 'application/octet-stream'}
|
||||
rq_kwargs['headers'] = headers
|
||||
|
||||
mock_rq.assert_called_once_with('/v1/images/detail', 'PUT',
|
||||
**rq_kwargs)
|
||||
|
||||
self.assertEqual(exp_resp, resp)
|
||||
|
||||
def test_raw_request_w_bad_content_length(self):
|
||||
with tempfile.NamedTemporaryFile() as test_file:
|
||||
test_file.write(b'abcd')
|
||||
test_file.seek(0)
|
||||
self.assertEqual(4, client_utils.get_file_size(test_file))
|
||||
|
||||
def mock_request(url, method, **kwargs):
|
||||
return kwargs
|
||||
|
||||
with mock.patch.object(self.client, '_http_request', mock_request):
|
||||
self.assertRaises(AttributeError, self.client.raw_request,
|
||||
'PUT', '/v1/images/detail', body=test_file,
|
||||
content_length=32)
|
||||
|
||||
def test_connection_refused_raw_request(self):
|
||||
"""
|
||||
Should receive a CommunicationError if connection refused.
|
||||
And the error should list the host and port that refused the
|
||||
connection
|
||||
"""
|
||||
endpoint = 'http://example.com:9292'
|
||||
client = http.HTTPClient(endpoint, token=u'abc123')
|
||||
http_client.HTTPConnection.request(mox.IgnoreArg(), mox.IgnoreArg(),
|
||||
headers=mox.IgnoreArg()
|
||||
).AndRaise(socket.error())
|
||||
self.mock.ReplayAll()
|
||||
try:
|
||||
client.raw_request('GET', '/v1/images/detail?limit=20')
|
||||
|
||||
self.fail('An exception should have bypassed this line.')
|
||||
except exc.CommunicationError as comm_err:
|
||||
fail_msg = ("Exception message '%s' should contain '%s'" %
|
||||
(comm_err.message, endpoint))
|
||||
self.assertTrue(endpoint in comm_err.message, fail_msg)
|
||||
resp, body = self.client.get('/v1/images/detail', headers=headers)
|
||||
self.assertEqual(resp, fake)
|
||||
|
||||
def test_parse_endpoint(self):
|
||||
endpoint = 'http://example.com:9292'
|
||||
@@ -313,81 +153,84 @@ class TestClient(testtools.TestCase):
|
||||
query='', fragment='')
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_get_connection_class(self):
|
||||
endpoint = 'http://example.com:9292'
|
||||
test_client = http.HTTPClient(endpoint, token=u'adc123')
|
||||
actual = (test_client.get_connection_class('https'))
|
||||
self.assertEqual(http.VerifiedHTTPSConnection, actual)
|
||||
|
||||
def test_get_connections_kwargs_http(self):
|
||||
endpoint = 'http://example.com:9292'
|
||||
test_client = http.HTTPClient(endpoint, token=u'adc123')
|
||||
actual = test_client.get_connection_kwargs('http', insecure=True)
|
||||
self.assertEqual({'timeout': 600.0}, actual)
|
||||
self.assertEqual(test_client.timeout, 600.0)
|
||||
|
||||
def test_get_connections_kwargs_https(self):
|
||||
endpoint = 'http://example.com:9292'
|
||||
test_client = http.HTTPClient(endpoint, token=u'adc123')
|
||||
actual = test_client.get_connection_kwargs('https', insecure=True)
|
||||
expected = {'cacert': None,
|
||||
'cert_file': None,
|
||||
'insecure': True,
|
||||
'key_file': None,
|
||||
'ssl_compression': True,
|
||||
'timeout': 600.0}
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_log_curl_request_with_non_ascii_char(self):
|
||||
try:
|
||||
headers = {'header1': 'value1\xa5\xa6'}
|
||||
http_client_object = http.HTTPClient(self.endpoint)
|
||||
http_client_object.log_curl_request('GET',
|
||||
'http://www.example.com/\xa5',
|
||||
{'headers': headers})
|
||||
except UnicodeDecodeError as e:
|
||||
self.fail("Unexpected UnicodeDecodeError exception '%s'" % e)
|
||||
|
||||
|
||||
class TestHostResolutionError(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestHostResolutionError, self).setUp()
|
||||
self.mock = mox.Mox()
|
||||
self.invalid_host = "example.com.incorrect_top_level_domain"
|
||||
|
||||
def test_incorrect_domain_error(self):
|
||||
"""
|
||||
Make sure that using a domain which does not resolve causes an
|
||||
exception which mentions that specific hostname as a reason for
|
||||
failure.
|
||||
"""
|
||||
class FailingConnectionClass(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def putrequest(self, *args, **kwargs):
|
||||
raise socket.gaierror(-2, "Name or service not known")
|
||||
|
||||
def request(self, *args, **kwargs):
|
||||
raise socket.gaierror(-2, "Name or service not known")
|
||||
|
||||
self.endpoint = 'http://%s:9292' % (self.invalid_host,)
|
||||
self.client = http.HTTPClient(self.endpoint, token=u'abc123')
|
||||
|
||||
self.mock.StubOutWithMock(self.client, 'get_connection')
|
||||
self.client.get_connection().AndReturn(FailingConnectionClass())
|
||||
def test_http_chunked_request(self):
|
||||
# Lets fake the response
|
||||
# returned by requests
|
||||
response = "Ok"
|
||||
data = six.StringIO(response)
|
||||
fake = utils.FakeResponse({}, data)
|
||||
requests.Session.request(
|
||||
mox.IgnoreArg(),
|
||||
mox.IgnoreArg(),
|
||||
stream=mox.IgnoreArg(),
|
||||
data=mox.IsA(types.GeneratorType),
|
||||
headers=mox.IgnoreArg()).AndReturn(fake)
|
||||
self.mock.ReplayAll()
|
||||
|
||||
try:
|
||||
self.client.raw_request('GET', '/example/path')
|
||||
self.fail("gaierror should be raised")
|
||||
except exc.InvalidEndpoint as e:
|
||||
self.assertTrue(self.invalid_host in str(e),
|
||||
"exception should contain the hostname")
|
||||
headers = {"test": u'chunked_request'}
|
||||
resp, body = self.client.post('/v1/images/',
|
||||
headers=headers, data=data)
|
||||
self.assertEqual(resp, fake)
|
||||
|
||||
def tearDown(self):
|
||||
super(TestHostResolutionError, self).tearDown()
|
||||
self.mock.UnsetStubs()
|
||||
def test_http_json(self):
|
||||
data = {"test": "json_request"}
|
||||
fake = utils.FakeResponse({}, "OK")
|
||||
|
||||
def test_json(passed_data):
|
||||
"""
|
||||
This function tests whether the data
|
||||
being passed to request's method is
|
||||
a valid json or not.
|
||||
|
||||
This function will be called by pymox
|
||||
|
||||
:params passed_data: The data being
|
||||
passed to requests.Session.request.
|
||||
"""
|
||||
if not isinstance(passed_data, six.string_types):
|
||||
return False
|
||||
|
||||
try:
|
||||
passed_data = json.loads(passed_data)
|
||||
return data == passed_data
|
||||
except (TypeError, ValueError):
|
||||
return False
|
||||
|
||||
requests.Session.request(
|
||||
mox.IgnoreArg(),
|
||||
mox.IgnoreArg(),
|
||||
stream=mox.IgnoreArg(),
|
||||
data=mox.Func(test_json),
|
||||
headers=mox.IgnoreArg()).AndReturn(fake)
|
||||
self.mock.ReplayAll()
|
||||
|
||||
headers = {"test": u'chunked_request'}
|
||||
resp, body = self.client.post('/v1/images/',
|
||||
headers=headers,
|
||||
data=data)
|
||||
self.assertEqual(resp, fake)
|
||||
|
||||
def test_http_chunked_response(self):
|
||||
headers = {"Content-Type": "application/octet-stream"}
|
||||
data = "TEST"
|
||||
fake = utils.FakeResponse(headers, six.StringIO(data))
|
||||
|
||||
requests.Session.request(
|
||||
mox.IgnoreArg(),
|
||||
mox.IgnoreArg(),
|
||||
stream=mox.IgnoreArg(),
|
||||
data=mox.IgnoreArg(),
|
||||
headers=mox.IgnoreArg()).AndReturn(fake)
|
||||
self.mock.ReplayAll()
|
||||
headers = {"test": u'chunked_request'}
|
||||
resp, body = self.client.get('/v1/images/')
|
||||
self.assertTrue(isinstance(body, types.GeneratorType))
|
||||
self.assertEqual([data], list(body))
|
||||
|
||||
|
||||
class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
@@ -396,7 +239,7 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
def test_setcontext_unable_to_load_cacert(self):
|
||||
"""Add this UT case with Bug#1265730."""
|
||||
self.assertRaises(exc.SSLConfigurationError,
|
||||
http.VerifiedHTTPSConnection,
|
||||
https.VerifiedHTTPSConnection,
|
||||
"127.0.0.1",
|
||||
None,
|
||||
None,
|
||||
@@ -405,45 +248,3 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
None,
|
||||
False,
|
||||
True)
|
||||
|
||||
|
||||
class TestResponseBodyIterator(testtools.TestCase):
|
||||
|
||||
def test_iter_default_chunk_size_64k(self):
|
||||
resp = utils.FakeResponse({}, six.BytesIO(b'X' * 98304))
|
||||
iterator = http.ResponseBodyIterator(resp)
|
||||
chunks = list(iterator)
|
||||
self.assertEqual([b'X' * 65536, b'X' * 32768], chunks)
|
||||
|
||||
def test_integrity_check_with_correct_checksum(self):
|
||||
resp = utils.FakeResponse({}, six.BytesIO(b'CCC'))
|
||||
body = http.ResponseBodyIterator(resp)
|
||||
body.set_checksum('defb99e69a9f1f6e06f15006b1f166ae')
|
||||
list(body)
|
||||
|
||||
def test_integrity_check_with_wrong_checksum(self):
|
||||
resp = utils.FakeResponse({}, six.BytesIO(b'BB'))
|
||||
body = http.ResponseBodyIterator(resp)
|
||||
body.set_checksum('wrong')
|
||||
try:
|
||||
list(body)
|
||||
self.fail('integrity checked passed with wrong checksum')
|
||||
except IOError as e:
|
||||
self.assertEqual(errno.EPIPE, e.errno)
|
||||
|
||||
def test_set_checksum_in_consumed_iterator(self):
|
||||
resp = utils.FakeResponse({}, six.BytesIO(b'CCC'))
|
||||
body = http.ResponseBodyIterator(resp)
|
||||
list(body)
|
||||
# Setting checksum for an already consumed iterator should raise an
|
||||
# AttributeError.
|
||||
self.assertRaises(
|
||||
AttributeError, body.set_checksum,
|
||||
'defb99e69a9f1f6e06f15006b1f166ae')
|
||||
|
||||
def test_body_size(self):
|
||||
size = 1000000007
|
||||
resp = utils.FakeResponse(
|
||||
{'content-length': str(size)}, six.BytesIO(b'BB'))
|
||||
body = http.ResponseBodyIterator(resp)
|
||||
self.assertEqual(size, len(body))
|
||||
|
||||
+19
-26
@@ -105,13 +105,11 @@ class ShellCacheSchemaTest(utils.TestCase):
|
||||
super(ShellCacheSchemaTest, self).setUp()
|
||||
self._mock_client_setup()
|
||||
self._mock_shell_setup()
|
||||
os.path.exists = mock.MagicMock()
|
||||
self.cache_dir = '/dir_for_cached_schema'
|
||||
self.cache_file = self.cache_dir + '/image_schema.json'
|
||||
|
||||
def tearDown(self):
|
||||
super(ShellCacheSchemaTest, self).tearDown()
|
||||
os.path.exists.reset_mock()
|
||||
|
||||
def _mock_client_setup(self):
|
||||
self.schema_dict = {
|
||||
@@ -137,27 +135,8 @@ class ShellCacheSchemaTest(utils.TestCase):
|
||||
return Args(args)
|
||||
|
||||
@mock.patch('six.moves.builtins.open', new=mock.mock_open(), create=True)
|
||||
def test_cache_schema_gets_when_not_exists(self):
|
||||
mocked_path_exists_result_lst = [True, False]
|
||||
os.path.exists.side_effect = \
|
||||
lambda *args: mocked_path_exists_result_lst.pop(0)
|
||||
|
||||
options = {
|
||||
'get_schema': False
|
||||
}
|
||||
|
||||
self.shell._cache_schema(self._make_args(options),
|
||||
home_dir=self.cache_dir)
|
||||
|
||||
self.assertEqual(4, open.mock_calls.__len__())
|
||||
self.assertEqual(mock.call(self.cache_file, 'w'), open.mock_calls[0])
|
||||
self.assertEqual(mock.call().write(json.dumps(self.schema_dict)),
|
||||
open.mock_calls[2])
|
||||
|
||||
@mock.patch('six.moves.builtins.open', new=mock.mock_open(), create=True)
|
||||
def test_cache_schema_gets_when_forced(self):
|
||||
os.path.exists.return_value = True
|
||||
|
||||
@mock.patch('os.path.exists', return_value=True)
|
||||
def test_cache_schema_gets_when_forced(self, exists_mock):
|
||||
options = {
|
||||
'get_schema': True
|
||||
}
|
||||
@@ -171,9 +150,23 @@ class ShellCacheSchemaTest(utils.TestCase):
|
||||
open.mock_calls[2])
|
||||
|
||||
@mock.patch('six.moves.builtins.open', new=mock.mock_open(), create=True)
|
||||
def test_cache_schema_leaves_when_present_not_forced(self):
|
||||
os.path.exists.return_value = True
|
||||
@mock.patch('os.path.exists', side_effect=[True, False])
|
||||
def test_cache_schema_gets_when_not_exists(self, exists_mock):
|
||||
options = {
|
||||
'get_schema': False
|
||||
}
|
||||
|
||||
self.shell._cache_schema(self._make_args(options),
|
||||
home_dir=self.cache_dir)
|
||||
|
||||
self.assertEqual(4, open.mock_calls.__len__())
|
||||
self.assertEqual(mock.call(self.cache_file, 'w'), open.mock_calls[0])
|
||||
self.assertEqual(mock.call().write(json.dumps(self.schema_dict)),
|
||||
open.mock_calls[2])
|
||||
|
||||
@mock.patch('six.moves.builtins.open', new=mock.mock_open(), create=True)
|
||||
@mock.patch('os.path.exists', return_value=True)
|
||||
def test_cache_schema_leaves_when_present_not_forced(self, exists_mock):
|
||||
options = {
|
||||
'get_schema': False
|
||||
}
|
||||
@@ -183,5 +176,5 @@ class ShellCacheSchemaTest(utils.TestCase):
|
||||
|
||||
os.path.exists.assert_any_call(self.cache_dir)
|
||||
os.path.exists.assert_any_call(self.cache_file)
|
||||
self.assertEqual(2, os.path.exists.call_count)
|
||||
self.assertEqual(2, exists_mock.call_count)
|
||||
self.assertEqual(0, open.mock_calls.__len__())
|
||||
|
||||
+57
-36
@@ -16,9 +16,11 @@
|
||||
import os
|
||||
|
||||
from OpenSSL import crypto
|
||||
from requests.packages.urllib3 import poolmanager
|
||||
import testtools
|
||||
|
||||
from glanceclient.common import http
|
||||
from glanceclient.common import https
|
||||
from glanceclient import exc
|
||||
|
||||
|
||||
@@ -26,6 +28,26 @@ TEST_VAR_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
'var'))
|
||||
|
||||
|
||||
class TestRequestsIntegration(testtools.TestCase):
|
||||
|
||||
def test_pool_patch(self):
|
||||
client = http.HTTPClient("https://localhost",
|
||||
ssl_compression=True)
|
||||
self.assertNotEqual(https.HTTPSConnectionPool,
|
||||
poolmanager.pool_classes_by_scheme["https"])
|
||||
|
||||
adapter = client.session.adapters.get("https://")
|
||||
self.assertFalse(isinstance(adapter, https.HTTPSAdapter))
|
||||
|
||||
client = http.HTTPClient("https://localhost",
|
||||
ssl_compression=False)
|
||||
self.assertEqual(https.HTTPSConnectionPool,
|
||||
poolmanager.pool_classes_by_scheme["https"])
|
||||
|
||||
adapter = client.session.adapters.get("https://")
|
||||
self.assertTrue(isinstance(adapter, https.HTTPSAdapter))
|
||||
|
||||
|
||||
class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
def test_ssl_init_ok(self):
|
||||
"""
|
||||
@@ -35,10 +57,10 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
cert_file = os.path.join(TEST_VAR_DIR, 'certificate.crt')
|
||||
cacert = os.path.join(TEST_VAR_DIR, 'ca.crt')
|
||||
try:
|
||||
http.VerifiedHTTPSConnection('127.0.0.1', 0,
|
||||
key_file=key_file,
|
||||
cert_file=cert_file,
|
||||
cacert=cacert)
|
||||
https.VerifiedHTTPSConnection('127.0.0.1', 0,
|
||||
key_file=key_file,
|
||||
cert_file=cert_file,
|
||||
cacert=cacert)
|
||||
except exc.SSLConfigurationError:
|
||||
self.fail('Failed to init VerifiedHTTPSConnection.')
|
||||
|
||||
@@ -49,9 +71,9 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
cert_file = os.path.join(TEST_VAR_DIR, 'certificate.crt')
|
||||
cacert = os.path.join(TEST_VAR_DIR, 'ca.crt')
|
||||
try:
|
||||
http.VerifiedHTTPSConnection('127.0.0.1', 0,
|
||||
cert_file=cert_file,
|
||||
cacert=cacert)
|
||||
https.VerifiedHTTPSConnection('127.0.0.1', 0,
|
||||
cert_file=cert_file,
|
||||
cacert=cacert)
|
||||
self.fail('Failed to raise assertion.')
|
||||
except exc.SSLConfigurationError:
|
||||
pass
|
||||
@@ -63,9 +85,9 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
key_file = os.path.join(TEST_VAR_DIR, 'privatekey.key')
|
||||
cacert = os.path.join(TEST_VAR_DIR, 'ca.crt')
|
||||
try:
|
||||
http.VerifiedHTTPSConnection('127.0.0.1', 0,
|
||||
key_file=key_file,
|
||||
cacert=cacert)
|
||||
https.VerifiedHTTPSConnection('127.0.0.1', 0,
|
||||
key_file=key_file,
|
||||
cacert=cacert)
|
||||
except exc.SSLConfigurationError:
|
||||
pass
|
||||
except Exception:
|
||||
@@ -78,9 +100,9 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
cert_file = os.path.join(TEST_VAR_DIR, 'certificate.crt')
|
||||
cacert = os.path.join(TEST_VAR_DIR, 'ca.crt')
|
||||
try:
|
||||
http.VerifiedHTTPSConnection('127.0.0.1', 0,
|
||||
cert_file=cert_file,
|
||||
cacert=cacert)
|
||||
https.VerifiedHTTPSConnection('127.0.0.1', 0,
|
||||
cert_file=cert_file,
|
||||
cacert=cacert)
|
||||
self.fail('Failed to raise assertion.')
|
||||
except exc.SSLConfigurationError:
|
||||
pass
|
||||
@@ -92,9 +114,9 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
cert_file = os.path.join(TEST_VAR_DIR, 'badcert.crt')
|
||||
cacert = os.path.join(TEST_VAR_DIR, 'ca.crt')
|
||||
try:
|
||||
http.VerifiedHTTPSConnection('127.0.0.1', 0,
|
||||
cert_file=cert_file,
|
||||
cacert=cacert)
|
||||
https.VerifiedHTTPSConnection('127.0.0.1', 0,
|
||||
cert_file=cert_file,
|
||||
cacert=cacert)
|
||||
self.fail('Failed to raise assertion.')
|
||||
except exc.SSLConfigurationError:
|
||||
pass
|
||||
@@ -106,9 +128,9 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
cert_file = os.path.join(TEST_VAR_DIR, 'certificate.crt')
|
||||
cacert = os.path.join(TEST_VAR_DIR, 'badca.crt')
|
||||
try:
|
||||
http.VerifiedHTTPSConnection('127.0.0.1', 0,
|
||||
cert_file=cert_file,
|
||||
cacert=cacert)
|
||||
https.VerifiedHTTPSConnection('127.0.0.1', 0,
|
||||
cert_file=cert_file,
|
||||
cacert=cacert)
|
||||
self.fail('Failed to raise assertion.')
|
||||
except exc.SSLConfigurationError:
|
||||
pass
|
||||
@@ -123,7 +145,7 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
# The expected cert should have CN=0.0.0.0
|
||||
self.assertEqual('0.0.0.0', cert.get_subject().commonName)
|
||||
try:
|
||||
conn = http.VerifiedHTTPSConnection('0.0.0.0', 0)
|
||||
conn = https.VerifiedHTTPSConnection('0.0.0.0', 0)
|
||||
conn.verify_callback(None, cert, 0, 0, 1)
|
||||
except Exception:
|
||||
self.fail('Unexpected exception.')
|
||||
@@ -138,7 +160,7 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
# The expected cert should have CN=*.pong.example.com
|
||||
self.assertEqual('*.pong.example.com', cert.get_subject().commonName)
|
||||
try:
|
||||
conn = http.VerifiedHTTPSConnection('ping.pong.example.com', 0)
|
||||
conn = https.VerifiedHTTPSConnection('ping.pong.example.com', 0)
|
||||
conn.verify_callback(None, cert, 0, 0, 1)
|
||||
except Exception:
|
||||
self.fail('Unexpected exception.')
|
||||
@@ -153,13 +175,13 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
# The expected cert should have CN=0.0.0.0
|
||||
self.assertEqual('0.0.0.0', cert.get_subject().commonName)
|
||||
try:
|
||||
conn = http.VerifiedHTTPSConnection('alt1.example.com', 0)
|
||||
conn = https.VerifiedHTTPSConnection('alt1.example.com', 0)
|
||||
conn.verify_callback(None, cert, 0, 0, 1)
|
||||
except Exception:
|
||||
self.fail('Unexpected exception.')
|
||||
|
||||
try:
|
||||
conn = http.VerifiedHTTPSConnection('alt2.example.com', 0)
|
||||
conn = https.VerifiedHTTPSConnection('alt2.example.com', 0)
|
||||
conn.verify_callback(None, cert, 0, 0, 1)
|
||||
except Exception:
|
||||
self.fail('Unexpected exception.')
|
||||
@@ -174,19 +196,19 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
# The expected cert should have CN=0.0.0.0
|
||||
self.assertEqual('0.0.0.0', cert.get_subject().commonName)
|
||||
try:
|
||||
conn = http.VerifiedHTTPSConnection('alt1.example.com', 0)
|
||||
conn = https.VerifiedHTTPSConnection('alt1.example.com', 0)
|
||||
conn.verify_callback(None, cert, 0, 0, 1)
|
||||
except Exception:
|
||||
self.fail('Unexpected exception.')
|
||||
|
||||
try:
|
||||
conn = http.VerifiedHTTPSConnection('alt2.example.com', 0)
|
||||
conn = https.VerifiedHTTPSConnection('alt2.example.com', 0)
|
||||
conn.verify_callback(None, cert, 0, 0, 1)
|
||||
except Exception:
|
||||
self.fail('Unexpected exception.')
|
||||
|
||||
try:
|
||||
conn = http.VerifiedHTTPSConnection('alt3.example.net', 0)
|
||||
conn = https.VerifiedHTTPSConnection('alt3.example.net', 0)
|
||||
conn.verify_callback(None, cert, 0, 0, 1)
|
||||
self.fail('Failed to raise assertion.')
|
||||
except exc.SSLCertificateError:
|
||||
@@ -202,7 +224,7 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
# The expected cert should have CN=0.0.0.0
|
||||
self.assertEqual('0.0.0.0', cert.get_subject().commonName)
|
||||
try:
|
||||
conn = http.VerifiedHTTPSConnection('mismatch.example.com', 0)
|
||||
conn = https.VerifiedHTTPSConnection('mismatch.example.com', 0)
|
||||
except Exception:
|
||||
self.fail('Failed to init VerifiedHTTPSConnection.')
|
||||
|
||||
@@ -220,10 +242,9 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
self.assertEqual('openstack.example.com',
|
||||
cert.get_subject().commonName)
|
||||
try:
|
||||
conn = http.VerifiedHTTPSConnection('openstack.example.com', 0)
|
||||
conn = https.VerifiedHTTPSConnection('openstack.example.com', 0)
|
||||
except Exception:
|
||||
self.fail('Failed to init VerifiedHTTPSConnection.')
|
||||
|
||||
self.assertRaises(exc.SSLCertificateError,
|
||||
conn.verify_callback, None, cert, 0, 0, 1)
|
||||
|
||||
@@ -236,7 +257,7 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
key_file = 'fake.key'
|
||||
self.assertRaises(
|
||||
exc.SSLConfigurationError,
|
||||
http.VerifiedHTTPSConnection, '127.0.0.1',
|
||||
https.VerifiedHTTPSConnection, '127.0.0.1',
|
||||
0, key_file=key_file,
|
||||
cert_file=cert_file, cacert=cacert)
|
||||
|
||||
@@ -248,7 +269,7 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
cert_file = os.path.join(TEST_VAR_DIR, 'certificate.crt')
|
||||
cacert = os.path.join(TEST_VAR_DIR, 'ca.crt')
|
||||
try:
|
||||
http.VerifiedHTTPSConnection(
|
||||
https.VerifiedHTTPSConnection(
|
||||
'127.0.0.1', 0,
|
||||
key_file=key_file,
|
||||
cert_file=cert_file,
|
||||
@@ -264,7 +285,7 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
cert_file = os.path.join(TEST_VAR_DIR, 'certificate.crt')
|
||||
cacert = os.path.join(TEST_VAR_DIR, 'ca.crt')
|
||||
try:
|
||||
http.VerifiedHTTPSConnection(
|
||||
https.VerifiedHTTPSConnection(
|
||||
'127.0.0.1', 0,
|
||||
key_file=key_file,
|
||||
cert_file=cert_file,
|
||||
@@ -286,9 +307,9 @@ class TestVerifiedHTTPSConnection(testtools.TestCase):
|
||||
cert_file = cert_file.encode('ascii', 'strict').decode('utf-8')
|
||||
cacert = cacert.encode('ascii', 'strict').decode('utf-8')
|
||||
try:
|
||||
http.VerifiedHTTPSConnection('127.0.0.1', 0,
|
||||
key_file=key_file,
|
||||
cert_file=cert_file,
|
||||
cacert=cacert)
|
||||
https.VerifiedHTTPSConnection('127.0.0.1', 0,
|
||||
key_file=key_file,
|
||||
cert_file=cert_file,
|
||||
cacert=cacert)
|
||||
except exc.SSLConfigurationError:
|
||||
self.fail('Failed to init VerifiedHTTPSConnection.')
|
||||
|
||||
+67
-59
@@ -14,64 +14,53 @@
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import requests
|
||||
import json
|
||||
import six
|
||||
import testtools
|
||||
|
||||
from glanceclient.common import http
|
||||
|
||||
|
||||
class FakeAPI(object):
|
||||
def __init__(self, fixtures):
|
||||
self.fixtures = fixtures
|
||||
self.calls = []
|
||||
|
||||
def _request(self, method, url, headers=None, body=None,
|
||||
def _request(self, method, url, headers=None, data=None,
|
||||
content_length=None):
|
||||
call = (method, url, headers or {}, body)
|
||||
call = (method, url, headers or {}, data)
|
||||
if content_length is not None:
|
||||
call = tuple(list(call) + [content_length])
|
||||
self.calls.append(call)
|
||||
return self.fixtures[url][method]
|
||||
fixture = self.fixtures[url][method]
|
||||
|
||||
def raw_request(self, *args, **kwargs):
|
||||
fixture = self._request(*args, **kwargs)
|
||||
resp = FakeResponse(fixture[0], six.StringIO(fixture[1]))
|
||||
body_iter = http.ResponseBodyIterator(resp)
|
||||
return resp, body_iter
|
||||
data = fixture[1]
|
||||
if isinstance(fixture[1], six.string_types):
|
||||
try:
|
||||
data = json.loads(fixture[1])
|
||||
except ValueError:
|
||||
data = six.StringIO(fixture[1])
|
||||
|
||||
def json_request(self, *args, **kwargs):
|
||||
fixture = self._request(*args, **kwargs)
|
||||
return FakeResponse(fixture[0]), fixture[1]
|
||||
return FakeResponse(fixture[0], fixture[1]), data
|
||||
|
||||
def client_request(self, method, url, **kwargs):
|
||||
if 'json' in kwargs and 'body' not in kwargs:
|
||||
kwargs['body'] = kwargs.pop('json')
|
||||
resp, body = self.json_request(method, url, **kwargs)
|
||||
resp.json = lambda: body
|
||||
resp.content = bool(body)
|
||||
return resp
|
||||
def get(self, *args, **kwargs):
|
||||
return self._request('GET', *args, **kwargs)
|
||||
|
||||
def head(self, url, **kwargs):
|
||||
return self.client_request("HEAD", url, **kwargs)
|
||||
def post(self, *args, **kwargs):
|
||||
return self._request('POST', *args, **kwargs)
|
||||
|
||||
def get(self, url, **kwargs):
|
||||
return self.client_request("GET", url, **kwargs)
|
||||
def put(self, *args, **kwargs):
|
||||
return self._request('PUT', *args, **kwargs)
|
||||
|
||||
def post(self, url, **kwargs):
|
||||
return self.client_request("POST", url, **kwargs)
|
||||
def patch(self, *args, **kwargs):
|
||||
return self._request('PATCH', *args, **kwargs)
|
||||
|
||||
def put(self, url, **kwargs):
|
||||
return self.client_request("PUT", url, **kwargs)
|
||||
def delete(self, *args, **kwargs):
|
||||
return self._request('DELETE', *args, **kwargs)
|
||||
|
||||
def delete(self, url, **kwargs):
|
||||
return self.raw_request("DELETE", url, **kwargs)
|
||||
|
||||
def patch(self, url, **kwargs):
|
||||
return self.client_request("PATCH", url, **kwargs)
|
||||
def head(self, *args, **kwargs):
|
||||
return self._request('HEAD', *args, **kwargs)
|
||||
|
||||
|
||||
class FakeResponse(object):
|
||||
class RawRequest(object):
|
||||
def __init__(self, headers, body=None,
|
||||
version=1.0, status=200, reason="Ok"):
|
||||
"""
|
||||
@@ -97,36 +86,55 @@ class FakeResponse(object):
|
||||
return self.body.read(amt)
|
||||
|
||||
|
||||
class FakeResponse(object):
|
||||
def __init__(self, headers=None, body=None,
|
||||
version=1.0, status_code=200, reason="Ok"):
|
||||
"""
|
||||
:param headers: dict representing HTTP response headers
|
||||
:param body: file-like object
|
||||
:param version: HTTP Version
|
||||
:param status: Response status code
|
||||
:param reason: Status code related message.
|
||||
"""
|
||||
self.body = body
|
||||
self.reason = reason
|
||||
self.version = version
|
||||
self.headers = headers
|
||||
self.status_code = status_code
|
||||
self.raw = RawRequest(headers, body=body, reason=reason,
|
||||
version=version, status=status_code)
|
||||
|
||||
@property
|
||||
def ok(self):
|
||||
return (self.status_code < 400 or
|
||||
self.status_code >= 600)
|
||||
|
||||
def read(self, amt):
|
||||
return self.body.read(amt)
|
||||
|
||||
@property
|
||||
def content(self):
|
||||
if hasattr(self.body, "read"):
|
||||
return self.body.read()
|
||||
return self.body
|
||||
|
||||
def json(self, **kwargs):
|
||||
return self.body and json.loads(self.content) or ""
|
||||
|
||||
def iter_content(self, chunk_size=1, decode_unicode=False):
|
||||
while True:
|
||||
chunk = self.raw.read(chunk_size)
|
||||
if not chunk:
|
||||
break
|
||||
yield chunk
|
||||
|
||||
|
||||
class TestCase(testtools.TestCase):
|
||||
TEST_REQUEST_BASE = {
|
||||
'config': {'danger_mode': False},
|
||||
'verify': True}
|
||||
|
||||
|
||||
class TestResponse(requests.Response):
|
||||
"""
|
||||
Class used to wrap requests.Response and provide some
|
||||
convenience to initialize with a dict
|
||||
"""
|
||||
def __init__(self, data):
|
||||
self._text = None
|
||||
super(TestResponse, self)
|
||||
if isinstance(data, dict):
|
||||
self.status_code = data.get('status_code', None)
|
||||
self.headers = data.get('headers', None)
|
||||
# Fake the text attribute to streamline Response creation
|
||||
self._text = data.get('text', None)
|
||||
else:
|
||||
self.status_code = data
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__dict__ == other.__dict__
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
return self._text
|
||||
|
||||
|
||||
class FakeTTYStdout(six.StringIO):
|
||||
"""A Fake stdout that try to emulate a TTY device as much as possible."""
|
||||
|
||||
|
||||
@@ -932,7 +932,7 @@ class ParameterFakeAPI(utils.FakeAPI):
|
||||
},
|
||||
]}
|
||||
|
||||
def json_request(self, method, url, **kwargs):
|
||||
def get(self, url, **kwargs):
|
||||
self.url = url
|
||||
return utils.FakeResponse({}), ParameterFakeAPI.image_list
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ class LegacyShellV1Test(testtools.TestCase):
|
||||
args = Image()
|
||||
gc = client.Client('1', 'http://is.invalid')
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint, test_shell.do_update, gc, args)
|
||||
exc.CommunicationError, test_shell.do_update, gc, args)
|
||||
|
||||
def test_do_update(self):
|
||||
class Image():
|
||||
|
||||
+16
-16
@@ -224,81 +224,81 @@ class ShellInvalidEndpointandParameterTest(utils.TestCase):
|
||||
|
||||
def test_image_list_invalid_endpoint(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint, self.run_command, 'image-list')
|
||||
exc.CommunicationError, self.run_command, 'image-list')
|
||||
|
||||
def test_image_details_invalid_endpoint_legacy(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint, self.run_command, 'details')
|
||||
exc.CommunicationError, self.run_command, 'details')
|
||||
|
||||
def test_image_update_invalid_endpoint_legacy(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint,
|
||||
exc.CommunicationError,
|
||||
self.run_command, 'update {"name":""test}')
|
||||
|
||||
def test_image_index_invalid_endpoint_legacy(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint,
|
||||
exc.CommunicationError,
|
||||
self.run_command, 'index')
|
||||
|
||||
def test_image_create_invalid_endpoint(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint,
|
||||
exc.CommunicationError,
|
||||
self.run_command, 'image-create')
|
||||
|
||||
def test_image_delete_invalid_endpoint(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint,
|
||||
exc.CommunicationError,
|
||||
self.run_command, 'image-delete <fake>')
|
||||
|
||||
def test_image_download_invalid_endpoint(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint,
|
||||
exc.CommunicationError,
|
||||
self.run_command, 'image-download <fake>')
|
||||
|
||||
def test_image_members_invalid_endpoint(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint,
|
||||
exc.CommunicationError,
|
||||
self.run_command, 'image-members fake_id')
|
||||
|
||||
def test_members_list_invalid_endpoint(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint,
|
||||
exc.CommunicationError,
|
||||
self.run_command, 'member-list --image-id fake')
|
||||
|
||||
def test_member_replace_invalid_endpoint(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint,
|
||||
exc.CommunicationError,
|
||||
self.run_command, 'members-replace image_id member_id')
|
||||
|
||||
def test_image_show_invalid_endpoint_legacy(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint, self.run_command, 'show image')
|
||||
exc.CommunicationError, self.run_command, 'show image')
|
||||
|
||||
def test_image_show_invalid_endpoint(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint,
|
||||
exc.CommunicationError,
|
||||
self.run_command, 'image-show --human-readable <IMAGE_ID>')
|
||||
|
||||
def test_member_images_invalid_endpoint_legacy(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint,
|
||||
exc.CommunicationError,
|
||||
self.run_command, 'member-images member_id')
|
||||
|
||||
def test_member_create_invalid_endpoint(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint,
|
||||
exc.CommunicationError,
|
||||
self.run_command,
|
||||
'member-create --can-share <IMAGE_ID> <TENANT_ID>')
|
||||
|
||||
def test_member_delete_invalid_endpoint(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint,
|
||||
exc.CommunicationError,
|
||||
self.run_command,
|
||||
'member-delete <IMAGE_ID> <TENANT_ID>')
|
||||
|
||||
def test_member_add_invalid_endpoint(self):
|
||||
self.assertRaises(
|
||||
exc.InvalidEndpoint,
|
||||
exc.CommunicationError,
|
||||
self.run_command,
|
||||
'member-add <IMAGE_ID> <TENANT_ID>')
|
||||
|
||||
|
||||
@@ -514,9 +514,11 @@ class TestController(testtools.TestCase):
|
||||
image_data = 'CCC'
|
||||
image_id = '606b0e88-7c5a-4d54-b5bb-046105d4de6f'
|
||||
self.controller.upload(image_id, image_data, image_size=3)
|
||||
body = {'image_data': image_data,
|
||||
'image_size': 3}
|
||||
expect = [('PUT', '/v2/images/%s/file' % image_id,
|
||||
{'Content-Type': 'application/octet-stream'},
|
||||
image_data, 3)]
|
||||
body)]
|
||||
self.assertEqual(expect, self.api.calls)
|
||||
|
||||
def test_data_without_checksum(self):
|
||||
|
||||
+12
-68
@@ -13,17 +13,12 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import mock
|
||||
import six
|
||||
import testtools
|
||||
|
||||
from glanceclient.common import http
|
||||
from glanceclient.common import progressbar
|
||||
from glanceclient.common import utils
|
||||
from glanceclient.v2 import shell as test_shell
|
||||
from tests import utils as test_utils
|
||||
|
||||
|
||||
class ShellV2Test(testtools.TestCase):
|
||||
@@ -208,16 +203,18 @@ class ShellV2Test(testtools.TestCase):
|
||||
utils.print_dict.assert_called_once_with({
|
||||
'id': 'pass', 'name': 'IMG-01', 'disk_format': 'vhd'})
|
||||
|
||||
def test_do_location_add_update_with_invalid_json_metadata(self):
|
||||
args = self._make_args({'id': 'pass',
|
||||
'url': 'http://foo/bar',
|
||||
'metadata': '{1, 2, 3}'})
|
||||
self.assert_exits_with_msg(test_shell.do_location_add,
|
||||
args,
|
||||
'Metadata is not a valid JSON object.')
|
||||
self.assert_exits_with_msg(test_shell.do_location_update,
|
||||
args,
|
||||
'Metadata is not a valid JSON object.')
|
||||
def test_do_explain(self):
|
||||
input = {
|
||||
'page_size': 18,
|
||||
'id': 'pass',
|
||||
'schemas': 'test',
|
||||
'model': 'test',
|
||||
}
|
||||
args = self._make_args(input)
|
||||
with mock.patch.object(utils, 'print_list'):
|
||||
test_shell.do_explain(self.gc, args)
|
||||
|
||||
self.gc.schemas.get.assert_called_once_with('test')
|
||||
|
||||
def test_do_location_add(self):
|
||||
gc = self.gc
|
||||
@@ -260,19 +257,6 @@ class ShellV2Test(testtools.TestCase):
|
||||
loc['metadata'])
|
||||
utils.print_dict.assert_called_once_with(expect_image)
|
||||
|
||||
def test_do_explain(self):
|
||||
input = {
|
||||
'page_size': 18,
|
||||
'id': 'pass',
|
||||
'schemas': 'test',
|
||||
'model': 'test',
|
||||
}
|
||||
args = self._make_args(input)
|
||||
with mock.patch.object(utils, 'print_list'):
|
||||
test_shell.do_explain(self.gc, args)
|
||||
|
||||
self.gc.schemas.get.assert_called_once_with('test')
|
||||
|
||||
def test_image_upload(self):
|
||||
args = self._make_args(
|
||||
{'id': 'IMG-01', 'file': 'test', 'size': 1024, 'progress': False})
|
||||
@@ -283,46 +267,6 @@ class ShellV2Test(testtools.TestCase):
|
||||
test_shell.do_image_upload(self.gc, args)
|
||||
mocked_upload.assert_called_once_with('IMG-01', 'testfile', 1024)
|
||||
|
||||
def test_image_upload_with_progressbar(self):
|
||||
args = self._make_args(
|
||||
{'id': 'IMG-01', 'file': 'test', 'size': 1024, 'progress': True})
|
||||
|
||||
with mock.patch.object(self.gc.images, 'upload') as mocked_upload:
|
||||
utils.get_data_file = mock.Mock(return_value='testfile')
|
||||
utils.get_file_size = mock.Mock(return_value=8)
|
||||
mocked_upload.return_value = None
|
||||
test_shell.do_image_upload(self.gc, args)
|
||||
self.assertIsInstance(mocked_upload.call_args[0][1],
|
||||
progressbar.VerboseFileWrapper)
|
||||
|
||||
def test_image_download(self):
|
||||
args = self._make_args(
|
||||
{'id': 'pass', 'file': 'test', 'progress': False})
|
||||
|
||||
with mock.patch.object(self.gc.images, 'data') as mocked_data:
|
||||
resp = test_utils.FakeResponse({}, six.StringIO('CCC'))
|
||||
ret = mocked_data.return_value = http.ResponseBodyIterator(resp)
|
||||
test_shell.do_image_download(self.gc, args)
|
||||
|
||||
mocked_data.assert_called_once_with('pass')
|
||||
utils.save_image.assert_called_once_with(ret, 'test')
|
||||
|
||||
def test_image_download_with_progressbar(self):
|
||||
args = self._make_args(
|
||||
{'id': 'pass', 'file': 'test', 'progress': True})
|
||||
|
||||
with mock.patch.object(self.gc.images, 'data') as mocked_data:
|
||||
resp = test_utils.FakeResponse({}, six.StringIO('CCC'))
|
||||
mocked_data.return_value = http.ResponseBodyIterator(resp)
|
||||
test_shell.do_image_download(self.gc, args)
|
||||
|
||||
mocked_data.assert_called_once_with('pass')
|
||||
utils.save_image.assert_called_once_with(mock.ANY, 'test')
|
||||
self.assertIsInstance(
|
||||
utils.save_image.call_args[0][0],
|
||||
progressbar.VerboseIteratorWrapper
|
||||
)
|
||||
|
||||
def test_do_image_delete(self):
|
||||
args = self._make_args({'id': 'pass', 'file': 'test'})
|
||||
with mock.patch.object(self.gc.images, 'delete') as mocked_delete:
|
||||
|
||||
Reference in New Issue
Block a user