Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
3edca4c
Provide new auto-generated layer for Bigtable.
Jan 4, 2018
a677959
Merge branch 'master' into bigtable-regen
Jan 4, 2018
d540349
Add retry for read rows
Feb 14, 2018
4aa5702
Fix line lengths
Feb 14, 2018
f7d4c69
Add parameter start_inclusive to _create_row_request
Feb 14, 2018
28892ce
Update unit test case for yield rows retry
Feb 14, 2018
8d141f0
Fix line lengths
Feb 14, 2018
902f11b
Edit table.py as requested changes
Feb 16, 2018
4afabb1
Added code review changes from @jonparrott.
Feb 19, 2018
79698b1
Fix parameter name on _RetryableReadRows class
Feb 22, 2018
9b778be
Add retry for Deadline Exceeded on read rows
Feb 23, 2018
cafc5ad
Fix line over-indented
Feb 23, 2018
f974752
Fix line over-indented
Feb 23, 2018
f7242f4
Merge commit '3edca4c3299e85d4346dae49232d540a2d34002c' of https://gi…
Feb 23, 2018
180d1d5
Merge commit 'a67795951bc9f490265eda7cf511e17d2cc70921' of https://gi…
Feb 23, 2018
f2e39e0
Working on gapic integration
Feb 23, 2018
13adfd6
Refactore yield_rows retry
Feb 23, 2018
ca69a2f
1. Moved last_scanned_row_key into YieldRowsData.
Feb 27, 2018
02c154e
Fix line over-indented
Feb 27, 2018
b6da7fa
Fix row_data.py
Feb 27, 2018
f1a2d92
Change row.py as requested
Feb 27, 2018
65c1c4b
Create new test case for yield_rows iterator failure
Feb 28, 2018
170a173
Fix for start_inclusive on retry read rows.
Mar 1, 2018
5da3fd3
Merge branch 'master' of https://github.com/zakons/google-cloud-pytho…
Mar 1, 2018
f7a58b5
Change bigtable_pb2 imports to use from gapic library.
Mar 2, 2018
c8f664b
Merge branch 'feature/read_rows_retry' of https://github.com/zakons/g…
Mar 6, 2018
d5f0d98
Working on gapic
Mar 6, 2018
32df7bd
Integrate bigtable GAPIC client
Mar 8, 2018
77ec950
Refactoring unit tests
Mar 27, 2018
efbf2f5
Refactor to use GAPIC client
Mar 27, 2018
4702e3c
Put back _generated files
Mar 27, 2018
cf3fb72
Fix _generated files
Mar 27, 2018
1ffda67
Fix line too long and remove unused variables
Mar 27, 2018
124de59
Fix instance.py and its unittest cases
Mar 27, 2018
0f69800
Add admin client.
Mar 30, 2018
022bd3f
Fix the gRPC stub paths.
Mar 30, 2018
5378926
Merge commit '022bd3f21fa98ea08b6f1af71a9afec71c524805' of https://gi…
Apr 2, 2018
5575990
Update bigtable admin client pb files and imports
Apr 4, 2018
ee98e67
Remove old client and related modules
Apr 4, 2018
37235c7
Remove _generated
Apr 4, 2018
fdc6db0
Fix test cases and update table.py
Apr 5, 2018
feb7d44
Remove unused code from test_table.py
Apr 5, 2018
aaa050e
Add grpc google iam v1 on setup.py on bigtable
Apr 6, 2018
2887c50
Update doc strings
Apr 9, 2018
eb69eff
Update docstrings
Apr 10, 2018
32a16c4
Change on routing_header to use to_grpc_metadata
Apr 11, 2018
078ca2b
Fix test cases on test_bigtable_instance_admin_client_pb2.py
Apr 11, 2018
c4a4482
Fix test cases on test_bigtable_instance_admin_client_pb2.py
Apr 11, 2018
07c4073
Fix test cases on test_bigtable_instance_admin_client_pb2.py
Apr 11, 2018
ee095bc
Fix line indention and remove unused variables
Apr 11, 2018
35b63d8
Fix missing whitespace after
Apr 11, 2018
70935f3
Merge branch 'master' of https://github.com/zakons/google-cloud-pytho…
Apr 11, 2018
f715b80
Update docs for bigtable changes
Apr 11, 2018
28426ad
Fix Unexpected indentation on table.py
Apr 11, 2018
e04b6b9
Add license header
Apr 13, 2018
16650cb
Merge branch 'master' of https://github.com/zakons/google-cloud-pytho…
Apr 13, 2018
2ae35af
Refactore GAPIC to use like old client
Apr 17, 2018
87dbe14
Fix unit test cases and refactor code for GAPIC client
Apr 18, 2018
3b69b0b
Fix credential issue with test cases
Apr 18, 2018
cc7413f
Remove _data_stub from Client
Apr 18, 2018
3a462d3
Fix coverage issues
Apr 18, 2018
0c8238c
Fix bigtable docs
Apr 19, 2018
ef47441
Merge branch 'master' of https://github.com/zakons/google-cloud-pytho…
Apr 19, 2018
b9a7d8d
Fix Definition list ends without a blank line
Apr 19, 2018
ff3316a
Fix docstring on list_instances
Apr 19, 2018
a74ff23
Fix the doc string on list_instances
Apr 19, 2018
e7c64ae
Fix Definition list ends without a blank line
Apr 19, 2018
a8d6f83
Remove custer doc from docs
Apr 19, 2018
b67ab6a
Added cluster
Apr 23, 2018
634f3b4
Add cluster.rst bigtable docs
Apr 23, 2018
480f26b
Merge branch 'master' of https://github.com/zakons/google-cloud-pytho…
Apr 23, 2018
a9b6d90
Add cluster in toctree
Apr 23, 2018
ab91e64
Fix required changes on doc strings
aneepct Apr 30, 2018
8bb0816
Fix required changes on doc strings
aneepct Apr 30, 2018
3702901
Merge branch 'master' of https://github.com/zakons/google-cloud-pytho…
aneepct Apr 30, 2018
8c29ca6
Fix requested review changes
aneepct May 2, 2018
730c1d4
Rebase with master
aneepct May 3, 2018
929b827
Update system tests
aneepct May 7, 2018
b0646af
Remove unused imports
aneepct May 8, 2018
316e59c
Added ClientWithProject on Client
aneepct May 8, 2018
1118493
Add credentials on unit test cases
aneepct May 8, 2018
6e9f95e
Add ClientWithProject on Client
aneepct May 9, 2018
0f880b1
Rebase with Master
aneepct May 9, 2018
3de1ca3
Rebase with master and add pull client with project
aneepct May 9, 2018
4765e3f
Make requested changes
aneepct May 9, 2018
5f03036
Remove unused import
aneepct May 9, 2018
17f6ac3
Fixing system teest
aneepct May 9, 2018
b45004f
Fix instance and system test
aneepct May 9, 2018
c3c9857
Create test case for updated instace
aneepct May 9, 2018
35d3199
Update instance and system tests
aneepct May 9, 2018
8749b94
Fixed required changes
aneepct May 11, 2018
efef394
Fix py lint errors
aneepct May 11, 2018
9e2df00
Move _make_credentials to _testing
aneepct May 11, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 44 additions & 21 deletions bigtable/google/cloud/bigtable/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,21 @@
"""


from google.cloud.bigtable.instance import Instance
from google.cloud.bigtable.instance import _EXISTING_INSTANCE_LOCATION_ID
from google.api_core.gapic_v1 import client_info

from google.cloud import bigtable_v2
from google.cloud import bigtable_admin_v2

from google.cloud.bigtable import __version__
from google.cloud.bigtable.instance import Instance
from google.cloud.bigtable.instance import _EXISTING_INSTANCE_LOCATION_ID

from google.cloud.client import ClientWithProject


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


class Client(object):
class Client(ClientWithProject):
"""Client for interacting with Google Cloud Bigtable API.

.. note::
Expand Down Expand Up @@ -81,6 +89,9 @@ class Client(object):
:raises: :class:`ValueError <exceptions.ValueError>` if both ``read_only``
and ``admin`` are :data:`True`
"""
_table_data_client = None
_table_admin_client = None
_instance_admin_client = None

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

# NOTE: We set the scopes **before** calling the parent constructor.
# It **may** use those scopes in ``with_scopes_if_required``.
self.project = project
self._read_only = bool(read_only)
self._admin = bool(admin)
self._channel = channel
self._credentials = credentials
self.SCOPE = self._get_scopes()
super(Client, self).__init__()
super(Client, self).__init__(project=project, credentials=credentials)

def _get_scopes(self):
"""Get the scopes corresponding to admin / read-only state.
Expand Down Expand Up @@ -130,21 +139,27 @@ def project_path(self):
:rtype: str
:returns: Return a fully-qualified project string.
"""
instance_client = self._instance_admin_client
instance_client = self.instance_admin_client
return instance_client.project_path(self.project)

@property
def _table_data_client(self):
def table_data_client(self):
"""Getter for the gRPC stub used for the Table Admin API.

:rtype: :class:`.bigtable_v2.BigtableClient`
:returns: A BigtableClient object.
"""
return bigtable_v2.BigtableClient(channel=self._channel,
credentials=self._credentials)
if self._table_data_client is None:
if not self._admin:
raise ValueError('Client is not an admin client.')
self._table_data_client = (
bigtable_v2.BigtableClient(credentials=self._credentials,
client_info=_CLIENT_INFO))

return self._table_data_client

@property
def _table_admin_client(self):
def table_admin_client(self):
"""Getter for the gRPC stub used for the Table Admin API.

:rtype: :class:`.bigtable_admin_pb2.BigtableTableAdmin`
Expand All @@ -153,13 +168,17 @@ def _table_admin_client(self):
client is not an admin client or if it has not been
:meth:`start`-ed.
"""
if not self._admin:
raise ValueError('Client is not an admin client.')
return bigtable_admin_v2.BigtableTableAdminClient(
channel=self._channel, credentials=self._credentials)
if self._table_admin_client is None:
if not self._admin:
raise ValueError('Client is not an admin client.')
self._table_admin_client = (
bigtable_admin_v2.BigtableTableAdminClient(
credentials=self._credentials, client_info=_CLIENT_INFO))

return self._table_admin_client

@property
def _instance_admin_client(self):
def instance_admin_client(self):
"""Getter for the gRPC stub used for the Table Admin API.

:rtype: :class:`.bigtable_admin_pb2.BigtableInstanceAdmin`
Expand All @@ -168,10 +187,14 @@ def _instance_admin_client(self):
client is not an admin client or if it has not been
:meth:`start`-ed.
"""
if not self._admin:
raise ValueError('Client is not an admin client.')
return bigtable_admin_v2.BigtableInstanceAdminClient(
channel=self._channel, credentials=self._credentials)
if self._instance_admin_client is None:
if not self._admin:
raise ValueError('Client is not an admin client.')
self._instance_admin_client = (
bigtable_admin_v2.BigtableInstanceAdminClient(
credentials=self._credentials, client_info=_CLIENT_INFO))

return self._instance_admin_client

def instance(self, instance_id, location=_EXISTING_INSTANCE_LOCATION_ID,
display_name=None):
Expand Down Expand Up @@ -202,4 +225,4 @@ def list_instances(self):
:rtype: :class:`~google.api_core.page_iterator.Iterator`
:returns: A list of Instance.
"""
return self._instance_admin_client.list_instances(self.project_path)
return self.instance_admin_client.list_instances(self.project_path)
10 changes: 5 additions & 5 deletions bigtable/google/cloud/bigtable/cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def name(self):
:rtype: str
:returns: The cluster name.
"""
return self._instance._client._instance_admin_client.cluster_path(
return self._instance._client.instance_admin_client.cluster_path(
self._instance._client.project, self._instance.instance_id,
self.cluster_id)

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

def reload(self):
"""Reload the metadata for this cluster."""
self._instance._client._instance_admin_client.get_cluster(self.name)
self._instance._client.instance_admin_client.get_cluster(self.name)

def create(self):
"""Create this cluster.
Expand All @@ -113,7 +113,7 @@ def create(self):
create operation.
"""
client = self._instance._client
return client._instance_admin_client.create_cluster(
return client.instance_admin_client.create_cluster(
self._instance.name, self.cluster_id, {})

def update(self, location='', serve_nodes=0):
Expand Down Expand Up @@ -147,7 +147,7 @@ def update(self, location='', serve_nodes=0):
update operation.
"""
client = self._instance._client
return client._instance_admin_client.update_cluster(
return client.instance_admin_client.update_cluster(
self.name, location, serve_nodes)

def delete(self):
Expand All @@ -171,4 +171,4 @@ def delete(self):
permanently deleted.
"""
client = self._instance._client
client._instance_admin_client.delete_cluster(self.name)
client.instance_admin_client.delete_cluster(self.name)
49 changes: 46 additions & 3 deletions bigtable/google/cloud/bigtable/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@
import re

from google.cloud.bigtable.table import Table
from google.cloud.bigtable.cluster import DEFAULT_SERVE_NODES

from google.cloud.bigtable_admin_v2 import enums
from google.cloud.bigtable_admin_v2.types import instance_pb2


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


class Instance(object):
Expand Down Expand Up @@ -59,15 +62,31 @@ class Instance(object):
Cloud Console UI. (Must be between 4 and 30
characters.) If this value is not set in the
constructor, will fall back to the instance ID.

:type serve_nodes: int
:param serve_nodes: (Optional) The number of nodes in the instance's
cluster; used to set up the instance's cluster.

:type default_storage_type: int
:param default_storage_type: (Optional) The default values are
STORAGE_TYPE_UNSPECIFIED = 0: The user did
not specify a storage type.
SSD = 1: Flash (SSD) storage should be
used.
HDD = 2: Magnetic drive (HDD) storage
should be used.
"""

def __init__(self, instance_id, client,
location_id=_EXISTING_INSTANCE_LOCATION_ID,
display_name=None):
display_name=None, serve_nodes=DEFAULT_SERVE_NODES,
default_storage_type=_STORAGE_TYPE_UNSPECIFIED):
self.instance_id = instance_id
self.display_name = display_name or instance_id
self._cluster_location_id = location_id
self._cluster_serve_nodes = serve_nodes
self._client = client
self._default_storage_type = default_storage_type

@classmethod
def from_pb(cls, instance_pb, client):
Expand Down Expand Up @@ -140,6 +159,15 @@ def __eq__(self, other):
def __ne__(self, other):
return not self == other

def reload(self):
"""Reload the metadata for this instance."""
instance_pb = self._client._instance_admin_client.get_instance(
self.name)

# NOTE: _update_from_pb does not check that the project and
# instance ID on the response match the request.
self._update_from_pb(instance_pb)

def create(self):
"""Create this instance.

Expand All @@ -160,10 +188,25 @@ def create(self):
:returns: The long-running operation corresponding to the create
operation.
"""
clusters = {}
cluster_id = '{}-cluster'.format(self.instance_id)
cluster_name = self._client._instance_admin_client.cluster_path(
self._client.project, self.instance_id, cluster_id)
location = self._client._instance_admin_client.location_path(
self._client.project, self._cluster_location_id)
cluster = instance_pb2.Cluster(
name=cluster_name, location=location,
serve_nodes=self._cluster_serve_nodes,
default_storage_type=self._default_storage_type)
instance = instance_pb2.Instance(
display_name=self.display_name
)
clusters[cluster_id] = cluster
parent = self._client.project_path

return self._client._instance_admin_client.create_instance(
parent=parent, instance_id=self.instance_id, instance={},
clusters={})
parent=parent, instance_id=self.instance_id, instance=instance,
clusters=clusters)

def update(self):
"""Update this instance.
Expand Down
16 changes: 9 additions & 7 deletions bigtable/tests/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,13 @@ def setUpModule():
if not Config.IN_EMULATOR:
retry = RetryErrors(GrpcRendezvous,
error_predicate=_retry_on_unavailable)
instances, failed_locations = retry(Config.CLIENT.list_instances)()

if len(failed_locations) != 0:
instances_response = retry(Config.CLIENT.list_instances)()

if len(instances_response.failed_locations) != 0:
raise ValueError('List instances failed in module set up.')

EXISTING_INSTANCES[:] = instances
EXISTING_INSTANCES[:] = instances_response.instances

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

def test_list_instances(self):
instances, failed_locations = Config.CLIENT.list_instances()
self.assertEqual(failed_locations, [])
instances_response = Config.CLIENT.list_instances()
self.assertEqual(instances_response.failed_locations, [])
# We have added one new instance in `setUpModule`.
self.assertEqual(len(instances), len(EXISTING_INSTANCES) + 1)
for instance in instances:
self.assertEqual(len(instances_response.instances),
len(EXISTING_INSTANCES) + 1)
for instance in instances_response.instances:
instance_existence = (instance in EXISTING_INSTANCES or
instance == Config.INSTANCE)
self.assertTrue(instance_existence)
Expand Down
14 changes: 14 additions & 0 deletions bigtable/tests/unit/_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,23 @@
"""Mocks used to emulate gRPC generated objects."""


import mock


class _FakeStub(object):
"""Acts as a gPRC stub."""

def __init__(self, *results):
self.results = results
self.method_calls = []


def _make_credentials():
import google.auth.credentials

class _CredentialsWithScopes(
google.auth.credentials.Credentials,
google.auth.credentials.Scoped):
pass

return mock.Mock(spec=_CredentialsWithScopes)
Loading