diff --git a/SoftLayer/API.py b/SoftLayer/API.py
index 322309f2b..2a79264c2 100644
--- a/SoftLayer/API.py
+++ b/SoftLayer/API.py
@@ -46,7 +46,8 @@ def create_client_from_env(username=None,
config_file=None,
proxy=None,
user_agent=None,
- transport=None):
+ transport=None,
+ verify=True):
"""Creates a SoftLayer API client using your environment.
Settings are loaded via keyword arguments, environemtal variables and
@@ -68,6 +69,8 @@ def create_client_from_env(username=None,
calls if you wish to bypass the packages built in User Agent string
:param transport: An object that's callable with this signature:
transport(SoftLayer.transports.Request)
+ :param bool verify: decide to verify the server's SSL/TLS cert. DO NOT SET
+ TO FALSE WITHOUT UNDERSTANDING THE IMPLICATIONS.
Usage:
@@ -83,6 +86,7 @@ def create_client_from_env(username=None,
endpoint_url=endpoint_url,
timeout=timeout,
proxy=proxy,
+ verify=verify,
config_file=config_file)
if transport is None:
@@ -94,6 +98,7 @@ def create_client_from_env(username=None,
proxy=settings.get('proxy'),
timeout=settings.get('timeout'),
user_agent=user_agent,
+ verify=verify,
)
else:
# Default the transport to use XMLRPC
@@ -102,6 +107,7 @@ def create_client_from_env(username=None,
proxy=settings.get('proxy'),
timeout=settings.get('timeout'),
user_agent=user_agent,
+ verify=verify,
)
# If we have enough information to make an auth driver, let's do it
@@ -240,7 +246,8 @@ def call(self, service, method, *args, **kwargs):
request.filter = kwargs.get('filter')
request.limit = kwargs.get('limit')
request.offset = kwargs.get('offset')
- request.verify = kwargs.get('verify')
+ if kwargs.get('verify') is not None:
+ request.verify = kwargs.get('verify')
if self.auth:
extra_headers = self.auth.get_headers()
diff --git a/SoftLayer/transports.py b/SoftLayer/transports.py
index 8e143818d..b3239fc14 100644
--- a/SoftLayer/transports.py
+++ b/SoftLayer/transports.py
@@ -64,7 +64,7 @@ def __init__(self):
self.transport_headers = {}
#: Boolean specifying if the server certificate should be verified.
- self.verify = True
+ self.verify = None
#: Client certificate file path.
self.cert = None
@@ -103,13 +103,15 @@ def __init__(self,
endpoint_url=None,
timeout=None,
proxy=None,
- user_agent=None):
+ user_agent=None,
+ verify=True):
self.endpoint_url = (endpoint_url or
consts.API_PUBLIC_ENDPOINT).rstrip('/')
self.timeout = timeout or None
self.proxy = proxy
self.user_agent = user_agent or consts.USER_AGENT
+ self.verify = verify
def __call__(self, request):
"""Makes a SoftLayer API call against the XML-RPC endpoint.
@@ -145,6 +147,12 @@ def __call__(self, request):
payload = utils.xmlrpc_client.dumps(tuple(largs),
methodname=request.method,
allow_none=True)
+
+ # Prefer the request setting, if it's not None
+ verify = request.verify
+ if verify is None:
+ verify = self.verify
+
LOGGER.debug("=== REQUEST ===")
LOGGER.info('POST %s', url)
LOGGER.debug(request.transport_headers)
@@ -155,7 +163,7 @@ def __call__(self, request):
data=payload,
headers=request.transport_headers,
timeout=self.timeout,
- verify=request.verify,
+ verify=verify,
cert=request.cert,
proxies=_proxies_dict(self.proxy))
LOGGER.debug("=== RESPONSE ===")
@@ -202,13 +210,15 @@ def __init__(self,
endpoint_url=None,
timeout=None,
proxy=None,
- user_agent=None):
+ user_agent=None,
+ verify=True):
self.endpoint_url = (endpoint_url or
consts.API_PUBLIC_ENDPOINT_REST).rstrip('/')
self.timeout = timeout or None
self.proxy = proxy
self.user_agent = user_agent or consts.USER_AGENT
+ self.verify = verify
def __call__(self, request):
"""Makes a SoftLayer API call against the REST endpoint.
@@ -269,6 +279,11 @@ def __call__(self, request):
url = '%s.%s' % ('/'.join(url_parts), 'json')
+ # Prefer the request setting, if it's not None
+ verify = request.verify
+ if verify is None:
+ verify = self.verify
+
LOGGER.debug("=== REQUEST ===")
LOGGER.info(url)
LOGGER.debug(request.transport_headers)
@@ -280,14 +295,14 @@ def __call__(self, request):
params=params,
data=raw_body,
timeout=self.timeout,
- verify=request.verify,
+ verify=verify,
cert=request.cert,
proxies=_proxies_dict(self.proxy))
LOGGER.debug("=== RESPONSE ===")
LOGGER.debug(resp.headers)
- LOGGER.debug(resp.content)
+ LOGGER.debug(resp.text)
resp.raise_for_status()
- result = json.loads(resp.content)
+ result = json.loads(resp.text)
if isinstance(result, list):
return SoftLayerListResult(
@@ -295,9 +310,9 @@ def __call__(self, request):
else:
return result
except requests.HTTPError as ex:
- content = json.loads(ex.response.content)
+ message = json.loads(ex.response.text)['error']
raise exceptions.SoftLayerAPIError(ex.response.status_code,
- content['error'])
+ message)
except requests.RequestException as ex:
raise exceptions.TransportError(0, str(ex))
diff --git a/setup.cfg b/setup.cfg
index 20520b557..ba4e6f120 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,4 +1,4 @@
-[pytest]
+[tool:pytest]
python_files = *_tests.py
[wheel]
diff --git a/tests/api_tests.py b/tests/api_tests.py
index ff5fce608..db42ee350 100644
--- a/tests/api_tests.py
+++ b/tests/api_tests.py
@@ -16,7 +16,8 @@ class Inititialization(testing.TestCase):
def test_init(self):
client = SoftLayer.Client(username='doesnotexist',
api_key='issurelywrong',
- timeout=10)
+ timeout=10,
+ endpoint_url='http://example.com/v3/xmlrpc/')
self.assertIsInstance(client.auth, SoftLayer.BasicAuthentication)
self.assertEqual(client.auth.username, 'doesnotexist')
@@ -95,7 +96,7 @@ def test_simple_call(self):
offset=None,
)
- def test_verify(self):
+ def test_verify_request_false(self):
client = SoftLayer.BaseClient(transport=self.mocks)
mock = self.set_mock('SoftLayer_SERVICE', 'METHOD')
mock.return_value = {"test": "result"}
@@ -105,6 +106,26 @@ def test_verify(self):
self.assertEqual(resp, {"test": "result"})
self.assert_called_with('SoftLayer_SERVICE', 'METHOD', verify=False)
+ def test_verify_request_true(self):
+ client = SoftLayer.BaseClient(transport=self.mocks)
+ mock = self.set_mock('SoftLayer_SERVICE', 'METHOD')
+ mock.return_value = {"test": "result"}
+
+ resp = client.call('SERVICE', 'METHOD', verify=True)
+
+ self.assertEqual(resp, {"test": "result"})
+ self.assert_called_with('SoftLayer_SERVICE', 'METHOD', verify=True)
+
+ def test_verify_request_not_specified(self):
+ client = SoftLayer.BaseClient(transport=self.mocks)
+ mock = self.set_mock('SoftLayer_SERVICE', 'METHOD')
+ mock.return_value = {"test": "result"}
+
+ resp = client.call('SERVICE', 'METHOD')
+
+ self.assertEqual(resp, {"test": "result"})
+ self.assert_called_with('SoftLayer_SERVICE', 'METHOD', verify=None)
+
@mock.patch('SoftLayer.API.BaseClient.iter_call')
def test_iterate(self, _iter_call):
self.client['SERVICE'].METHOD(iter=True)
diff --git a/tests/transport_tests.py b/tests/transport_tests.py
index e262b7eb0..690882f69 100644
--- a/tests/transport_tests.py
+++ b/tests/transport_tests.py
@@ -8,6 +8,7 @@
import warnings
import mock
+import pytest
import requests
import six
@@ -17,26 +18,31 @@
from SoftLayer import transports
+def get_xmlrpc_response():
+ response = requests.Response()
+ list_body = six.b('''
+
+
+
+
+
+
+
+
+''')
+ response.raw = io.BytesIO(list_body)
+ response.headers['SoftLayer-Total-Items'] = 10
+ response.status_code = 200
+ return response
+
+
class TestXmlRpcAPICall(testing.TestCase):
def set_up(self):
self.transport = transports.XmlRpcTransport(
endpoint_url='http://something.com',
)
- self.response = requests.Response()
- list_body = six.b('''
-
-
-
-
-
-
-
-
-''')
- self.response.raw = io.BytesIO(list_body)
- self.response.headers['SoftLayer-Total-Items'] = 10
- self.response.status_code = 200
+ self.response = get_xmlrpc_response()
@mock.patch('requests.request')
def test_call(self, request):
@@ -251,6 +257,55 @@ def test_request_exception(self, request):
self.assertRaises(SoftLayer.TransportError, self.transport, req)
+@mock.patch('requests.request')
+@pytest.mark.parametrize(
+ "transport_verify,request_verify,expected",
+ [
+ (True, True, True),
+ (True, False, False),
+ (True, None, True),
+
+ (False, True, True),
+ (False, False, False),
+ (False, None, False),
+
+ (None, True, True),
+ (None, False, False),
+ (None, None, True),
+ ]
+)
+def test_verify(request,
+ transport_verify,
+ request_verify,
+ expected):
+ request.return_value = get_xmlrpc_response()
+
+ transport = transports.XmlRpcTransport(
+ endpoint_url='http://something.com',
+ )
+
+ req = transports.Request()
+ req.service = 'SoftLayer_Service'
+ req.method = 'getObject'
+
+ if request_verify is not None:
+ req.verify = request_verify
+
+ if transport_verify is not None:
+ transport.verify = transport_verify
+
+ transport(req)
+
+ request.assert_called_with('POST',
+ 'http://something.com/SoftLayer_Service',
+ data=mock.ANY,
+ headers=mock.ANY,
+ cert=mock.ANY,
+ proxies=mock.ANY,
+ timeout=mock.ANY,
+ verify=expected)
+
+
class TestRestAPICall(testing.TestCase):
def set_up(self):
@@ -261,6 +316,7 @@ def set_up(self):
@mock.patch('requests.request')
def test_basic(self, request):
request().content = '[]'
+ request().text = '[]'
request().headers = requests.structures.CaseInsensitiveDict({
'SoftLayer-Total-Items': '10',
})
@@ -290,7 +346,7 @@ def test_error(self, request):
e = requests.HTTPError('error')
e.response = mock.MagicMock()
e.response.status_code = 404
- e.response.content = '''{
+ e.response.text = '''{
"error": "description",
"code": "Error Code"
}'''
@@ -315,7 +371,7 @@ def test_proxy_without_protocol(self):
@mock.patch('requests.request')
def test_valid_proxy(self, request):
- request().content = '{}'
+ request().text = '{}'
self.transport.proxy = 'http://localhost:3128'
req = transports.Request()
@@ -323,6 +379,7 @@ def test_valid_proxy(self, request):
req.method = 'Resource'
self.transport(req)
+
request.assert_called_with(
'GET', 'http://something.com/SoftLayer_Service/Resource.json',
proxies={'https': 'http://localhost:3128',
@@ -337,7 +394,7 @@ def test_valid_proxy(self, request):
@mock.patch('requests.request')
def test_with_id(self, request):
- request().content = '{}'
+ request().text = '{}'
req = transports.Request()
req.service = 'SoftLayer_Service'
@@ -361,7 +418,7 @@ def test_with_id(self, request):
@mock.patch('requests.request')
def test_with_args(self, request):
- request().content = '{}'
+ request().text = '{}'
req = transports.Request()
req.service = 'SoftLayer_Service'
@@ -385,7 +442,7 @@ def test_with_args(self, request):
@mock.patch('requests.request')
def test_with_filter(self, request):
- request().content = '{}'
+ request().text = '{}'
req = transports.Request()
req.service = 'SoftLayer_Service'
@@ -410,7 +467,7 @@ def test_with_filter(self, request):
@mock.patch('requests.request')
def test_with_mask(self, request):
- request().content = '{}'
+ request().text = '{}'
req = transports.Request()
req.service = 'SoftLayer_Service'