Skip to content

Commit 49c6e7c

Browse files
committed
Updating Bigtable system test to work with emulator.
Currently does not work as-is since most of the methods (most notable row.commit()) result in UNAVAILABLE from the backend.
1 parent 31e3369 commit 49c6e7c

File tree

6 files changed

+219
-39
lines changed

6 files changed

+219
-39
lines changed

gcloud/bigtable/client.py

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@
2727
"""
2828

2929

30+
import os
31+
3032
from pkg_resources import get_distribution
3133

34+
from gcloud._helpers import make_insecure_stub
3235
from gcloud._helpers import make_secure_stub
3336
from gcloud.bigtable._generated import bigtable_instance_admin_pb2
3437
from gcloud.bigtable._generated import bigtable_pb2
@@ -40,6 +43,7 @@
4043
from gcloud.client import _ClientFactoryMixin
4144
from gcloud.client import _ClientProjectMixin
4245
from gcloud.credentials import get_credentials
46+
from gcloud.environment_vars import BIGTABLE_EMULATOR
4347

4448

4549
TABLE_ADMIN_HOST = 'bigtableadmin.googleapis.com'
@@ -74,8 +78,12 @@ def _make_data_stub(client):
7478
:rtype: :class:`._generated.bigtable_pb2.BigtableStub`
7579
:returns: A gRPC stub object.
7680
"""
77-
return make_secure_stub(client.credentials, client.user_agent,
78-
bigtable_pb2.BigtableStub, DATA_API_HOST)
81+
if client.emulator_host is None:
82+
return make_secure_stub(client.credentials, client.user_agent,
83+
bigtable_pb2.BigtableStub, DATA_API_HOST)
84+
else:
85+
return make_insecure_stub(bigtable_pb2.BigtableStub,
86+
client.emulator_host)
7987

8088

8189
def _make_instance_stub(client):
@@ -87,10 +95,15 @@ def _make_instance_stub(client):
8795
:rtype: :class:`.bigtable_instance_admin_pb2.BigtableInstanceAdminStub`
8896
:returns: A gRPC stub object.
8997
"""
90-
return make_secure_stub(
91-
client.credentials, client.user_agent,
92-
bigtable_instance_admin_pb2.BigtableInstanceAdminStub,
93-
INSTANCE_ADMIN_HOST)
98+
if client.emulator_host is None:
99+
return make_secure_stub(
100+
client.credentials, client.user_agent,
101+
bigtable_instance_admin_pb2.BigtableInstanceAdminStub,
102+
INSTANCE_ADMIN_HOST)
103+
else:
104+
return make_insecure_stub(
105+
bigtable_instance_admin_pb2.BigtableInstanceAdminStub,
106+
client.emulator_host)
94107

95108

96109
def _make_operations_stub(client):
@@ -105,9 +118,13 @@ def _make_operations_stub(client):
105118
:rtype: :class:`._generated.operations_grpc_pb2.OperationsStub`
106119
:returns: A gRPC stub object.
107120
"""
108-
return make_secure_stub(client.credentials, client.user_agent,
109-
operations_grpc_pb2.OperationsStub,
110-
OPERATIONS_API_HOST)
121+
if client.emulator_host is None:
122+
return make_secure_stub(client.credentials, client.user_agent,
123+
operations_grpc_pb2.OperationsStub,
124+
OPERATIONS_API_HOST)
125+
else:
126+
return make_insecure_stub(operations_grpc_pb2.OperationsStub,
127+
client.emulator_host)
111128

112129

113130
def _make_table_stub(client):
@@ -119,9 +136,14 @@ def _make_table_stub(client):
119136
:rtype: :class:`.bigtable_instance_admin_pb2.BigtableTableAdminStub`
120137
:returns: A gRPC stub object.
121138
"""
122-
return make_secure_stub(client.credentials, client.user_agent,
123-
bigtable_table_admin_pb2.BigtableTableAdminStub,
124-
TABLE_ADMIN_HOST)
139+
if client.emulator_host is None:
140+
return make_secure_stub(client.credentials, client.user_agent,
141+
bigtable_table_admin_pb2.BigtableTableAdminStub,
142+
TABLE_ADMIN_HOST)
143+
else:
144+
return make_insecure_stub(
145+
bigtable_table_admin_pb2.BigtableTableAdminStub,
146+
client.emulator_host)
125147

126148

127149
class Client(_ClientFactoryMixin, _ClientProjectMixin):
@@ -192,6 +214,7 @@ def __init__(self, project=None, credentials=None,
192214
pass
193215
self._credentials = credentials
194216
self.user_agent = user_agent
217+
self.emulator_host = os.getenv(BIGTABLE_EMULATOR)
195218

196219
# Create gRPC stubs for making requests.
197220
self._data_stub = _make_data_stub(self)

gcloud/environment_vars.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
PUBSUB_EMULATOR = 'PUBSUB_EMULATOR_HOST'
3434
"""Environment variable defining host for Pub/Sub emulator."""
3535

36+
BIGTABLE_EMULATOR = 'BIGTABLE_EMULATOR_HOST'
37+
"""Environment variable defining host for Bigtable emulator."""
38+
3639
CREDENTIALS = 'GOOGLE_APPLICATION_CREDENTIALS'
3740
"""Environment variable defining location of Google credentials."""
3841

system_tests/bigtable.py

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import datetime
1616
import operator
17+
import os
1718

1819
import unittest
1920

@@ -29,10 +30,12 @@
2930
from gcloud.bigtable.row_filters import RowFilterUnion
3031
from gcloud.bigtable.row_data import Cell
3132
from gcloud.bigtable.row_data import PartialRowData
33+
from gcloud.environment_vars import BIGTABLE_EMULATOR
3234
from gcloud.environment_vars import TESTS_PROJECT
3335

3436
from retry import RetryErrors
3537
from retry import RetryResult
38+
from system_test_utils import EmulatorCreds
3639
from system_test_utils import unique_resource_id
3740

3841

@@ -61,6 +64,7 @@ class Config(object):
6164
"""
6265
CLIENT = None
6366
INSTANCE = None
67+
IN_EMULATOR = False
6468

6569

6670
def _wait_until_complete(operation, max_attempts=5):
@@ -93,29 +97,43 @@ def _retry_on_unavailable(exc):
9397
def setUpModule():
9498
from grpc._channel import _Rendezvous
9599
_helpers.PROJECT = TESTS_PROJECT
96-
Config.CLIENT = Client(admin=True)
100+
Config.IN_EMULATOR = os.getenv(BIGTABLE_EMULATOR) is not None
101+
102+
if Config.IN_EMULATOR:
103+
credentials = EmulatorCreds()
104+
Config.CLIENT = Client(credentials=credentials, admin=True)
105+
else:
106+
Config.CLIENT = Client(admin=True)
107+
97108
Config.INSTANCE = Config.CLIENT.instance(INSTANCE_ID, LOCATION_ID)
98-
retry = RetryErrors(_Rendezvous, error_predicate=_retry_on_unavailable)
99-
instances, failed_locations = retry(Config.CLIENT.list_instances)()
100109

101-
if len(failed_locations) != 0:
102-
raise ValueError('List instances failed in module set up.')
110+
if not Config.IN_EMULATOR:
111+
retry = RetryErrors(_Rendezvous, error_predicate=_retry_on_unavailable)
112+
instances, failed_locations = retry(Config.CLIENT.list_instances)()
113+
114+
if len(failed_locations) != 0:
115+
raise ValueError('List instances failed in module set up.')
103116

104-
EXISTING_INSTANCES[:] = instances
117+
EXISTING_INSTANCES[:] = instances
105118

106-
# After listing, create the test instance.
107-
created_op = Config.INSTANCE.create()
108-
if not _wait_until_complete(created_op):
109-
raise RuntimeError('Instance creation exceed 5 seconds.')
119+
# After listing, create the test instance.
120+
created_op = Config.INSTANCE.create()
121+
if not _wait_until_complete(created_op):
122+
raise RuntimeError('Instance creation exceed 5 seconds.')
110123

111124

112125
def tearDownModule():
113-
Config.INSTANCE.delete()
126+
if not Config.IN_EMULATOR:
127+
Config.INSTANCE.delete()
114128

115129

116130
class TestInstanceAdminAPI(unittest.TestCase):
117131

118132
def setUp(self):
133+
if Config.IN_EMULATOR:
134+
self.skipTest(
135+
'Instance Admin API not supported by Bigtable emulator')
136+
119137
self.instances_to_delete = []
120138

121139
def tearDown(self):
@@ -186,9 +204,14 @@ def setUpClass(cls):
186204

187205
@classmethod
188206
def tearDownClass(cls):
189-
cls._table.delete()
207+
if not Config.IN_EMULATOR:
208+
cls._table.delete()
190209

191210
def setUp(self):
211+
if Config.IN_EMULATOR:
212+
self.skipTest(
213+
'Table Admin API not supported by Bigtable emulator')
214+
192215
self.tables_to_delete = []
193216

194217
def tearDown(self):
@@ -291,8 +314,8 @@ def setUpClass(cls):
291314

292315
@classmethod
293316
def tearDownClass(cls):
294-
# Will also delete any data contained in the table.
295-
cls._table.delete()
317+
if not Config.IN_EMULATOR:
318+
cls._table.delete()
296319

297320
def setUp(self):
298321
self.rows_to_delete = []

system_tests/run_emulator.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,24 @@
2525

2626
import psutil
2727

28+
from gcloud.environment_vars import BIGTABLE_EMULATOR
2829
from gcloud.environment_vars import GCD_DATASET
2930
from gcloud.environment_vars import GCD_HOST
3031
from gcloud.environment_vars import PUBSUB_EMULATOR
3132
from run_system_test import run_module_tests
3233

3334

3435
PACKAGE_INFO = {
36+
'bigtable': (BIGTABLE_EMULATOR,),
3537
'datastore': (GCD_DATASET, GCD_HOST),
36-
'pubsub': (PUBSUB_EMULATOR,)
38+
'pubsub': (PUBSUB_EMULATOR,),
3739
}
3840
EXTRA = {
3941
'datastore': ('--no-legacy',),
4042
}
4143
_DS_READY_LINE = '[datastore] Dev App Server is now running.\n'
4244
_PS_READY_LINE_PREFIX = '[pubsub] INFO: Server started, listening on '
45+
_BT_READY_LINE_PREFIX = '[bigtable] Cloud Bigtable emulator running on '
4346

4447

4548
def get_parser():
@@ -51,7 +54,7 @@ def get_parser():
5154
parser = argparse.ArgumentParser(
5255
description='Run GCloud system tests against local emulator.')
5356
parser.add_argument('--package', dest='package',
54-
choices=('datastore', 'pubsub'),
57+
choices=sorted(PACKAGE_INFO.keys()),
5558
default='datastore', help='Package to be tested.')
5659
return parser
5760

@@ -95,16 +98,33 @@ def datastore_wait_ready(popen):
9598
emulator_ready = popen.stderr.readline() == _DS_READY_LINE
9699

97100

98-
def pubsub_wait_ready(popen):
99-
"""Wait until the pubsub emulator is ready to use.
101+
def _prefix_wait_ready(popen, prefix):
102+
"""Wait until a process outputs a line with a given prefix.
100103
101104
:type popen: :class:`subprocess.Popen`
102105
:param popen: An open subprocess to interact with.
103106
"""
104107
emulator_ready = False
105108
while not emulator_ready:
106-
emulator_ready = popen.stderr.readline().startswith(
107-
_PS_READY_LINE_PREFIX)
109+
emulator_ready = popen.stderr.readline().startswith(prefix)
110+
111+
112+
def pubsub_wait_ready(popen):
113+
"""Wait until the pubsub emulator is ready to use.
114+
115+
:type popen: :class:`subprocess.Popen`
116+
:param popen: An open subprocess to interact with.
117+
"""
118+
_prefix_wait_ready(popen, _PS_READY_LINE_PREFIX)
119+
120+
121+
def bigtable_wait_ready(popen):
122+
"""Wait until the Bigtable emulator is ready to use.
123+
124+
:type popen: :class:`subprocess.Popen`
125+
:param popen: An open subprocess to interact with.
126+
"""
127+
_prefix_wait_ready(popen, _BT_READY_LINE_PREFIX)
108128

109129

110130
def wait_ready(package, popen):
@@ -117,14 +137,16 @@ def wait_ready(package, popen):
117137
:param popen: An open subprocess to interact with.
118138
119139
:raises: :class:`KeyError` if the ``package`` is not among
120-
``datastore``, ``pubsub``.
140+
``datastore``, ``pubsub``, ``bigtable``.
121141
"""
122142
if package == 'datastore':
123143
datastore_wait_ready(popen)
124144
elif package == 'pubsub':
125145
pubsub_wait_ready(popen)
146+
elif package == 'bigtable':
147+
bigtable_wait_ready(popen)
126148
else:
127-
raise KeyError('')
149+
raise KeyError(package)
128150

129151

130152
def cleanup(pid):

tox.ini

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,11 @@ commands =
137137
python {toxinidir}/system_tests/run_emulator.py --package=pubsub
138138
passenv = GCLOUD_*
139139
deps = {[testenv:datastore-emulator]deps}
140+
141+
[testenv:bigtable-emulator]
142+
basepython =
143+
python2.7
144+
commands =
145+
python {toxinidir}/system_tests/run_emulator.py --package=bigtable
146+
passenv = GCLOUD_*
147+
deps = {[testenv:datastore-emulator]deps}

0 commit comments

Comments
 (0)