Skip to content

Commit 45a81eb

Browse files
aneepcttseaver
authored andcommitted
BigTable: Modify system test for new GAPIC code (#5302)
* Provide new auto-generated layer for Bigtable. * Change bigtable_pb2 imports to use from gapic library. * Add retry for read rows * Add parameter start_inclusive to _create_row_request * Add retry for Deadline Exceeded on read rows * Refactor yield_rows retry * Add grpc google iam v1 on setup.py on bigtable * Change routing_header to use to_grpc_metadata
1 parent 9617a26 commit 45a81eb

File tree

11 files changed

+601
-284
lines changed

11 files changed

+601
-284
lines changed

packages/google-cloud-bigtable/google/cloud/bigtable/client.py

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,21 @@
2929
"""
3030

3131

32-
from google.cloud.bigtable.instance import Instance
33-
from google.cloud.bigtable.instance import _EXISTING_INSTANCE_LOCATION_ID
32+
from google.api_core.gapic_v1 import client_info
3433

3534
from google.cloud import bigtable_v2
3635
from google.cloud import bigtable_admin_v2
3736

37+
from google.cloud.bigtable import __version__
38+
from google.cloud.bigtable.instance import Instance
39+
from google.cloud.bigtable.instance import _EXISTING_INSTANCE_LOCATION_ID
40+
41+
from google.cloud.client import ClientWithProject
3842

43+
44+
_CLIENT_INFO = client_info.ClientInfo(
45+
client_library_version=__version__)
46+
SPANNER_ADMIN_SCOPE = 'https://www.googleapis.com/auth/spanner.admin'
3947
ADMIN_SCOPE = 'https://www.googleapis.com/auth/bigtable.admin'
4048
"""Scope for interacting with the Cluster Admin and Table Admin APIs."""
4149
DATA_SCOPE = 'https://www.googleapis.com/auth/bigtable.data'
@@ -44,7 +52,7 @@
4452
"""Scope for reading table data."""
4553

4654

47-
class Client(object):
55+
class Client(ClientWithProject):
4856
"""Client for interacting with Google Cloud Bigtable API.
4957
5058
.. note::
@@ -81,6 +89,9 @@ class Client(object):
8189
:raises: :class:`ValueError <exceptions.ValueError>` if both ``read_only``
8290
and ``admin`` are :data:`True`
8391
"""
92+
_table_data_client = None
93+
_table_admin_client = None
94+
_instance_admin_client = None
8495

8596
def __init__(self, project=None, credentials=None,
8697
read_only=False, admin=False, channel=None):
@@ -90,13 +101,11 @@ def __init__(self, project=None, credentials=None,
90101

91102
# NOTE: We set the scopes **before** calling the parent constructor.
92103
# It **may** use those scopes in ``with_scopes_if_required``.
93-
self.project = project
94104
self._read_only = bool(read_only)
95105
self._admin = bool(admin)
96106
self._channel = channel
97-
self._credentials = credentials
98107
self.SCOPE = self._get_scopes()
99-
super(Client, self).__init__()
108+
super(Client, self).__init__(project=project, credentials=credentials)
100109

101110
def _get_scopes(self):
102111
"""Get the scopes corresponding to admin / read-only state.
@@ -130,21 +139,27 @@ def project_path(self):
130139
:rtype: str
131140
:returns: Return a fully-qualified project string.
132141
"""
133-
instance_client = self._instance_admin_client
142+
instance_client = self.instance_admin_client
134143
return instance_client.project_path(self.project)
135144

136145
@property
137-
def _table_data_client(self):
146+
def table_data_client(self):
138147
"""Getter for the gRPC stub used for the Table Admin API.
139148
140149
:rtype: :class:`.bigtable_v2.BigtableClient`
141150
:returns: A BigtableClient object.
142151
"""
143-
return bigtable_v2.BigtableClient(channel=self._channel,
144-
credentials=self._credentials)
152+
if self._table_data_client is None:
153+
if not self._admin:
154+
raise ValueError('Client is not an admin client.')
155+
self._table_data_client = (
156+
bigtable_v2.BigtableClient(credentials=self._credentials,
157+
client_info=_CLIENT_INFO))
158+
159+
return self._table_data_client
145160

146161
@property
147-
def _table_admin_client(self):
162+
def table_admin_client(self):
148163
"""Getter for the gRPC stub used for the Table Admin API.
149164
150165
:rtype: :class:`.bigtable_admin_pb2.BigtableTableAdmin`
@@ -153,13 +168,17 @@ def _table_admin_client(self):
153168
client is not an admin client or if it has not been
154169
:meth:`start`-ed.
155170
"""
156-
if not self._admin:
157-
raise ValueError('Client is not an admin client.')
158-
return bigtable_admin_v2.BigtableTableAdminClient(
159-
channel=self._channel, credentials=self._credentials)
171+
if self._table_admin_client is None:
172+
if not self._admin:
173+
raise ValueError('Client is not an admin client.')
174+
self._table_admin_client = (
175+
bigtable_admin_v2.BigtableTableAdminClient(
176+
credentials=self._credentials, client_info=_CLIENT_INFO))
177+
178+
return self._table_admin_client
160179

161180
@property
162-
def _instance_admin_client(self):
181+
def instance_admin_client(self):
163182
"""Getter for the gRPC stub used for the Table Admin API.
164183
165184
:rtype: :class:`.bigtable_admin_pb2.BigtableInstanceAdmin`
@@ -168,10 +187,14 @@ def _instance_admin_client(self):
168187
client is not an admin client or if it has not been
169188
:meth:`start`-ed.
170189
"""
171-
if not self._admin:
172-
raise ValueError('Client is not an admin client.')
173-
return bigtable_admin_v2.BigtableInstanceAdminClient(
174-
channel=self._channel, credentials=self._credentials)
190+
if self._instance_admin_client is None:
191+
if not self._admin:
192+
raise ValueError('Client is not an admin client.')
193+
self._instance_admin_client = (
194+
bigtable_admin_v2.BigtableInstanceAdminClient(
195+
credentials=self._credentials, client_info=_CLIENT_INFO))
196+
197+
return self._instance_admin_client
175198

176199
def instance(self, instance_id, location=_EXISTING_INSTANCE_LOCATION_ID,
177200
display_name=None):
@@ -202,4 +225,4 @@ def list_instances(self):
202225
:rtype: :class:`~google.api_core.page_iterator.Iterator`
203226
:returns: A list of Instance.
204227
"""
205-
return self._instance_admin_client.list_instances(self.project_path)
228+
return self.instance_admin_client.list_instances(self.project_path)

packages/google-cloud-bigtable/google/cloud/bigtable/cluster.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def name(self):
6969
:rtype: str
7070
:returns: The cluster name.
7171
"""
72-
return self._instance._client._instance_admin_client.cluster_path(
72+
return self._instance._client.instance_admin_client.cluster_path(
7373
self._instance._client.project, self._instance.instance_id,
7474
self.cluster_id)
7575

@@ -90,7 +90,7 @@ def __ne__(self, other):
9090

9191
def reload(self):
9292
"""Reload the metadata for this cluster."""
93-
self._instance._client._instance_admin_client.get_cluster(self.name)
93+
self._instance._client.instance_admin_client.get_cluster(self.name)
9494

9595
def create(self):
9696
"""Create this cluster.
@@ -113,7 +113,7 @@ def create(self):
113113
create operation.
114114
"""
115115
client = self._instance._client
116-
return client._instance_admin_client.create_cluster(
116+
return client.instance_admin_client.create_cluster(
117117
self._instance.name, self.cluster_id, {})
118118

119119
def update(self, location='', serve_nodes=0):
@@ -147,7 +147,7 @@ def update(self, location='', serve_nodes=0):
147147
update operation.
148148
"""
149149
client = self._instance._client
150-
return client._instance_admin_client.update_cluster(
150+
return client.instance_admin_client.update_cluster(
151151
self.name, location, serve_nodes)
152152

153153
def delete(self):
@@ -171,4 +171,4 @@ def delete(self):
171171
permanently deleted.
172172
"""
173173
client = self._instance._client
174-
client._instance_admin_client.delete_cluster(self.name)
174+
client.instance_admin_client.delete_cluster(self.name)

packages/google-cloud-bigtable/google/cloud/bigtable/instance.py

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@
1818
import re
1919

2020
from google.cloud.bigtable.table import Table
21+
from google.cloud.bigtable.cluster import DEFAULT_SERVE_NODES
2122

2223
from google.cloud.bigtable_admin_v2 import enums
24+
from google.cloud.bigtable_admin_v2.types import instance_pb2
2325

2426

2527
_EXISTING_INSTANCE_LOCATION_ID = 'see-existing-cluster'
2628
_INSTANCE_NAME_RE = re.compile(r'^projects/(?P<project>[^/]+)/'
2729
r'instances/(?P<instance_id>[a-z][-a-z0-9]*)$')
30+
_STORAGE_TYPE_UNSPECIFIED = enums.StorageType.STORAGE_TYPE_UNSPECIFIED
2831

2932

3033
class Instance(object):
@@ -59,15 +62,31 @@ class Instance(object):
5962
Cloud Console UI. (Must be between 4 and 30
6063
characters.) If this value is not set in the
6164
constructor, will fall back to the instance ID.
65+
66+
:type serve_nodes: int
67+
:param serve_nodes: (Optional) The number of nodes in the instance's
68+
cluster; used to set up the instance's cluster.
69+
70+
:type default_storage_type: int
71+
:param default_storage_type: (Optional) The default values are
72+
STORAGE_TYPE_UNSPECIFIED = 0: The user did
73+
not specify a storage type.
74+
SSD = 1: Flash (SSD) storage should be
75+
used.
76+
HDD = 2: Magnetic drive (HDD) storage
77+
should be used.
6278
"""
6379

6480
def __init__(self, instance_id, client,
6581
location_id=_EXISTING_INSTANCE_LOCATION_ID,
66-
display_name=None):
82+
display_name=None, serve_nodes=DEFAULT_SERVE_NODES,
83+
default_storage_type=_STORAGE_TYPE_UNSPECIFIED):
6784
self.instance_id = instance_id
6885
self.display_name = display_name or instance_id
6986
self._cluster_location_id = location_id
87+
self._cluster_serve_nodes = serve_nodes
7088
self._client = client
89+
self._default_storage_type = default_storage_type
7190

7291
@classmethod
7392
def from_pb(cls, instance_pb, client):
@@ -140,6 +159,15 @@ def __eq__(self, other):
140159
def __ne__(self, other):
141160
return not self == other
142161

162+
def reload(self):
163+
"""Reload the metadata for this instance."""
164+
instance_pb = self._client._instance_admin_client.get_instance(
165+
self.name)
166+
167+
# NOTE: _update_from_pb does not check that the project and
168+
# instance ID on the response match the request.
169+
self._update_from_pb(instance_pb)
170+
143171
def create(self):
144172
"""Create this instance.
145173
@@ -160,10 +188,25 @@ def create(self):
160188
:returns: The long-running operation corresponding to the create
161189
operation.
162190
"""
191+
clusters = {}
192+
cluster_id = '{}-cluster'.format(self.instance_id)
193+
cluster_name = self._client._instance_admin_client.cluster_path(
194+
self._client.project, self.instance_id, cluster_id)
195+
location = self._client._instance_admin_client.location_path(
196+
self._client.project, self._cluster_location_id)
197+
cluster = instance_pb2.Cluster(
198+
name=cluster_name, location=location,
199+
serve_nodes=self._cluster_serve_nodes,
200+
default_storage_type=self._default_storage_type)
201+
instance = instance_pb2.Instance(
202+
display_name=self.display_name
203+
)
204+
clusters[cluster_id] = cluster
163205
parent = self._client.project_path
206+
164207
return self._client._instance_admin_client.create_instance(
165-
parent=parent, instance_id=self.instance_id, instance={},
166-
clusters={})
208+
parent=parent, instance_id=self.instance_id, instance=instance,
209+
clusters=clusters)
167210

168211
def update(self):
169212
"""Update this instance.

packages/google-cloud-bigtable/tests/system.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,13 @@ def setUpModule():
8686
if not Config.IN_EMULATOR:
8787
retry = RetryErrors(GrpcRendezvous,
8888
error_predicate=_retry_on_unavailable)
89-
instances, failed_locations = retry(Config.CLIENT.list_instances)()
9089

91-
if len(failed_locations) != 0:
90+
instances_response = retry(Config.CLIENT.list_instances)()
91+
92+
if len(instances_response.failed_locations) != 0:
9293
raise ValueError('List instances failed in module set up.')
9394

94-
EXISTING_INSTANCES[:] = instances
95+
EXISTING_INSTANCES[:] = instances_response.instances
9596

9697
# After listing, create the test instance.
9798
created_op = Config.INSTANCE.create()
@@ -116,11 +117,12 @@ def tearDown(self):
116117
instance.delete()
117118

118119
def test_list_instances(self):
119-
instances, failed_locations = Config.CLIENT.list_instances()
120-
self.assertEqual(failed_locations, [])
120+
instances_response = Config.CLIENT.list_instances()
121+
self.assertEqual(instances_response.failed_locations, [])
121122
# We have added one new instance in `setUpModule`.
122-
self.assertEqual(len(instances), len(EXISTING_INSTANCES) + 1)
123-
for instance in instances:
123+
self.assertEqual(len(instances_response.instances),
124+
len(EXISTING_INSTANCES) + 1)
125+
for instance in instances_response.instances:
124126
instance_existence = (instance in EXISTING_INSTANCES or
125127
instance == Config.INSTANCE)
126128
self.assertTrue(instance_existence)

packages/google-cloud-bigtable/tests/unit/_testing.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,23 @@
1515
"""Mocks used to emulate gRPC generated objects."""
1616

1717

18+
import mock
19+
20+
1821
class _FakeStub(object):
1922
"""Acts as a gPRC stub."""
2023

2124
def __init__(self, *results):
2225
self.results = results
2326
self.method_calls = []
27+
28+
29+
def _make_credentials():
30+
import google.auth.credentials
31+
32+
class _CredentialsWithScopes(
33+
google.auth.credentials.Credentials,
34+
google.auth.credentials.Scoped):
35+
pass
36+
37+
return mock.Mock(spec=_CredentialsWithScopes)

0 commit comments

Comments
 (0)