Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit fcd6280

Browse files
committed
Take VCAP_SERVICES env as arg in client builder
Rely on the client passing a valid VCAP_SERVICES variable rather than pulling it from the environment.
1 parent 4c285be commit fcd6280

File tree

3 files changed

+88
-63
lines changed

3 files changed

+88
-63
lines changed

src/cloudant/__init__.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,21 @@ def cloudant(user, passwd, **kwargs):
6363
cloudant_session.disconnect()
6464

6565
@contextlib.contextmanager
66-
def cloudant_bluemix(bm_service_name=None, **kwargs):
66+
def cloudant_bluemix(vcap_services, instance_name=None, **kwargs):
6767
"""
6868
Provides a context manager to create a Cloudant session and provide access
6969
to databases, docs etc.
7070
71-
:param str bm_service_name: Optional Bluemix service instance name. Only
72-
required if multiple Cloudant services are available.
71+
:param vcap_services: VCAP_SERVICES environment variable
72+
:type vcap_services: dict or str
73+
:param str instance_name: Optional Bluemix instance name. Only required if
74+
multiple Cloudant instances are available.
7375
:param str encoder: Optional json Encoder object used to encode
7476
documents for storage. Defaults to json.JSONEncoder.
7577
76-
Loads all configuration from the VCAP_SERVICES Cloud Foundry environment
77-
variable. The VCAP_SERVICES variable contains connection information to
78-
access a service instance. For example:
78+
Loads all configuration from the specified VCAP_SERVICES Cloud Foundry
79+
environment variable. The VCAP_SERVICES variable contains connection
80+
information to access a service instance. For example:
7981
8082
.. code-block:: json
8183
@@ -102,8 +104,23 @@ def cloudant_bluemix(bm_service_name=None, **kwargs):
102104
103105
See `Cloud Foundry Environment Variables <http://docs.cloudfoundry.org/
104106
devguide/deploy-apps/environment-variable.html#VCAP-SERVICES>`_.
107+
108+
Example usage:
109+
110+
.. code-block:: python
111+
112+
import os
113+
114+
# cloudant_bluemix context manager
115+
from cloudant import cloudant_bluemix
116+
117+
with cloudant_bluemix(os.getenv('VCAP_SERVICES'), 'Cloudant NoSQL DB') as client:
118+
# Context handles connect() and disconnect() for you.
119+
# Perform library operations within this context. Such as:
120+
print client.all_dbs()
121+
# ...
105122
"""
106-
service = CloudFoundryService(bm_service_name)
123+
service = CloudFoundryService(vcap_services, instance_name)
107124
cloudant_session = Cloudant(
108125
username=service.username,
109126
password=service.password,

src/cloudant/_common_util.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
throughout the library.
1818
"""
1919

20-
import os
2120
import sys
2221
import platform
2322
from collections import Sequence
@@ -340,9 +339,12 @@ def request(self, method, url, **kwargs):
340339
class CloudFoundryService(object):
341340
""" Manages Cloud Foundry service configuration. """
342341

343-
def __init__(self, name=None):
342+
def __init__(self, vcap_services, name=None):
344343
try:
345-
services = json.loads(os.getenv('VCAP_SERVICES', '{}'))
344+
services = vcap_services
345+
if not isinstance(vcap_services, dict):
346+
services = json.loads(vcap_services)
347+
346348
cloudant_services = services.get('cloudantNoSQLDB', [])
347349

348350
# use first service if no name given and only one service present

tests/unit/cloud_foundry_tests.py

Lines changed: 59 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
"""
2020

2121
import json
22-
import mock
2322
import unittest
2423

2524
from cloudant._common_util import CloudFoundryService
@@ -93,94 +92,101 @@ def __init__(self, *args, **kwargs):
9392
}
9493
]})
9594

96-
@mock.patch('os.getenv')
97-
def test_get_vcap_service_default_success(self, m_getenv):
98-
m_getenv.return_value = self._test_vcap_services_single
99-
service = CloudFoundryService()
95+
def test_get_vcap_service_default_success(self):
96+
service = CloudFoundryService(self._test_vcap_services_single)
10097
self.assertEqual('Cloudant NoSQL DB 1', service.name)
10198

102-
@mock.patch('os.getenv')
103-
def test_get_vcap_service_default_failure_multiple_services(self, m_getenv):
104-
m_getenv.return_value = self._test_vcap_services_multiple
99+
def test_get_vcap_service_default_success_as_dict(self):
100+
service = CloudFoundryService(
101+
json.loads(self._test_vcap_services_single)
102+
)
103+
self.assertEqual('Cloudant NoSQL DB 1', service.name)
104+
105+
def test_get_vcap_service_default_failure_multiple_services(self):
105106
with self.assertRaises(CloudantException) as cm:
106-
CloudFoundryService()
107+
CloudFoundryService(self._test_vcap_services_multiple)
107108
self.assertEqual('Missing service in VCAP_SERVICES', str(cm.exception))
108109

109-
@mock.patch('os.getenv')
110-
def test_get_vcap_service_instance_host(self, m_getenv):
111-
m_getenv.return_value = self._test_vcap_services_multiple
112-
service = CloudFoundryService('Cloudant NoSQL DB 1')
110+
def test_get_vcap_service_instance_host(self):
111+
service = CloudFoundryService(
112+
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 1'
113+
)
113114
self.assertEqual('example.cloudant.com', service.host)
114115

115-
@mock.patch('os.getenv')
116-
def test_get_vcap_service_instance_password(self, m_getenv):
117-
m_getenv.return_value = self._test_vcap_services_multiple
118-
service = CloudFoundryService('Cloudant NoSQL DB 1')
116+
def test_get_vcap_service_instance_password(self):
117+
service = CloudFoundryService(
118+
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 1'
119+
)
119120
self.assertEqual('pa$$w0rd01', service.password)
120121

121-
@mock.patch('os.getenv')
122-
def test_get_vcap_service_instance_port(self, m_getenv):
123-
m_getenv.return_value = self._test_vcap_services_multiple
124-
service = CloudFoundryService('Cloudant NoSQL DB 1')
122+
def test_get_vcap_service_instance_port(self):
123+
service = CloudFoundryService(
124+
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 1'
125+
)
125126
self.assertEqual('1234', service.port)
126127

127-
@mock.patch('os.getenv')
128-
def test_get_vcap_service_instance_port_default(self, m_getenv):
129-
m_getenv.return_value = self._test_vcap_services_multiple
130-
service = CloudFoundryService('Cloudant NoSQL DB 2')
128+
def test_get_vcap_service_instance_port_default(self):
129+
service = CloudFoundryService(
130+
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 2'
131+
)
131132
self.assertEqual('443', service.port)
132133

133-
@mock.patch('os.getenv')
134-
def test_get_vcap_service_instance_url(self, m_getenv):
135-
m_getenv.return_value = self._test_vcap_services_multiple
136-
service = CloudFoundryService('Cloudant NoSQL DB 1')
134+
def test_get_vcap_service_instance_url(self):
135+
service = CloudFoundryService(
136+
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 1'
137+
)
137138
self.assertEqual('https://example.cloudant.com:1234', service.url)
138139

139-
@mock.patch('os.getenv')
140-
def test_get_vcap_service_instance_username(self, m_getenv):
141-
m_getenv.return_value = self._test_vcap_services_multiple
142-
service = CloudFoundryService('Cloudant NoSQL DB 1')
140+
def test_get_vcap_service_instance_username(self):
141+
service = CloudFoundryService(
142+
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 1'
143+
)
143144
self.assertEqual('example', service.username)
144145

145-
@mock.patch('os.getenv')
146-
def test_raise_error_for_missing_host(self, m_getenv):
147-
m_getenv.return_value = self._test_vcap_services_multiple
146+
def test_raise_error_for_missing_host(self):
148147
with self.assertRaises(CloudantException):
149-
CloudFoundryService('Cloudant NoSQL DB 3')
148+
CloudFoundryService(
149+
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 3'
150+
)
150151

151-
@mock.patch('os.getenv')
152-
def test_raise_error_for_missing_password(self, m_getenv):
153-
m_getenv.return_value = self._test_vcap_services_multiple
152+
def test_raise_error_for_missing_password(self):
154153
with self.assertRaises(CloudantException) as cm:
155-
CloudFoundryService('Cloudant NoSQL DB 4')
154+
CloudFoundryService(
155+
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 4'
156+
)
156157
self.assertEqual(
157158
"Invalid service: 'password' missing",
158159
str(cm.exception)
159160
)
160161

161-
@mock.patch('os.getenv')
162-
def test_raise_error_for_missing_username(self, m_getenv):
163-
m_getenv.return_value = self._test_vcap_services_multiple
162+
def test_raise_error_for_missing_username(self):
164163
with self.assertRaises(CloudantException) as cm:
165-
CloudFoundryService('Cloudant NoSQL DB 5')
164+
CloudFoundryService(
165+
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 5'
166+
)
166167
self.assertEqual(
167168
"Invalid service: 'username' missing",
168169
str(cm.exception)
169170
)
170171

171-
@mock.patch('os.getenv')
172-
def test_raise_error_for_invalid_credentials_type(self, m_getenv):
173-
m_getenv.return_value = self._test_vcap_services_multiple
172+
def test_raise_error_for_invalid_credentials_type(self):
174173
with self.assertRaises(CloudantException) as cm:
175-
CloudFoundryService('Cloudant NoSQL DB 6')
174+
CloudFoundryService(
175+
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 6'
176+
)
176177
self.assertEqual(
177178
'Failed to decode VCAP_SERVICES service credentials',
178179
str(cm.exception)
179180
)
180181

181-
@mock.patch('os.getenv')
182-
def test_raise_error_for_missing_service(self, m_getenv):
183-
m_getenv.return_value = self._test_vcap_services_multiple
182+
def test_raise_error_for_missing_service(self):
184183
with self.assertRaises(CloudantException) as cm:
185-
CloudFoundryService('Cloudant NoSQL DB 7')
184+
CloudFoundryService(
185+
self._test_vcap_services_multiple, 'Cloudant NoSQL DB 7'
186+
)
186187
self.assertEqual('Missing service in VCAP_SERVICES', str(cm.exception))
188+
189+
def test_raise_error_for_invalid_vcap(self):
190+
with self.assertRaises(CloudantException) as cm:
191+
CloudFoundryService('{', 'Cloudant NoSQL DB 1') # invalid JSON
192+
self.assertEqual('Failed to decode VCAP_SERVICES JSON', str(cm.exception))

0 commit comments

Comments
 (0)