diff --git a/_testing/grpc/beta/interfaces.py b/_testing/grpc/beta/interfaces.py new file mode 100644 index 000000000000..c098733431fd --- /dev/null +++ b/_testing/grpc/beta/interfaces.py @@ -0,0 +1,13 @@ +# Copyright 2016 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/_testing/grpc/framework/interfaces/face/face.py b/_testing/grpc/framework/interfaces/face/face.py new file mode 100644 index 000000000000..c098733431fd --- /dev/null +++ b/_testing/grpc/framework/interfaces/face/face.py @@ -0,0 +1,13 @@ +# Copyright 2016 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/gcloud/bigtable/happybase/connection.py b/gcloud/bigtable/happybase/connection.py index c6ba0b2f8f96..bc57b3429eed 100644 --- a/gcloud/bigtable/happybase/connection.py +++ b/gcloud/bigtable/happybase/connection.py @@ -20,6 +20,14 @@ import six +from grpc.beta import interfaces +from grpc.framework.interfaces.face import face + +try: + from happybase.hbase.ttypes import AlreadyExists +except ImportError: + from gcloud.exceptions import Conflict as AlreadyExists + from gcloud.bigtable.client import Client from gcloud.bigtable.column_family import GCRuleIntersection from gcloud.bigtable.column_family import MaxAgeGCRule @@ -338,7 +346,13 @@ def create_table(self, name, families): # Create table instance and then make API calls. name = self._table_name(name) low_level_table = _LowLevelTable(name, self._cluster) - low_level_table.create() + try: + low_level_table.create() + except face.NetworkError as network_err: + if network_err.code == interfaces.StatusCode.ALREADY_EXISTS: + raise AlreadyExists(name) + else: + raise for column_family_name, gc_rule in gc_rule_dict.items(): column_family = low_level_table.column_family( diff --git a/gcloud/bigtable/happybase/test_connection.py b/gcloud/bigtable/happybase/test_connection.py index a07e9d86b7a2..2c96b9d1721b 100644 --- a/gcloud/bigtable/happybase/test_connection.py +++ b/gcloud/bigtable/happybase/test_connection.py @@ -13,6 +13,8 @@ # limitations under the License. +import sys + import unittest2 @@ -395,6 +397,55 @@ def test_create_table_bad_value(self): with self.assertRaises(ValueError): connection.create_table(name, families) + def _create_table_error_helper(self, err_val, err_type): + from gcloud._testing import _Monkey + from gcloud.bigtable.happybase import connection as MUT + + cluster = _Cluster() # Avoid implicit environ check. + connection = self._makeOne(autoconnect=False, cluster=cluster) + + tables_created = [] + + def make_table(*args, **kwargs): + kwargs['create_error'] = err_val + result = _MockLowLevelTable(*args, **kwargs) + tables_created.append(result) + return result + + name = 'table-name' + families = {'foo': {}} + with _Monkey(MUT, _LowLevelTable=make_table): + with self.assertRaises(err_type): + connection.create_table(name, families) + + self.assertEqual(len(tables_created), 1) + self.assertEqual(tables_created[0].create_calls, 1) + + @unittest2.skipUnless(sys.version_info[:2] == (2, 7), + 'gRPC only in Python 2.7') + def test_create_table_already_exists(self): + from grpc.beta import interfaces + from grpc.framework.interfaces.face import face + from gcloud.bigtable.happybase.connection import AlreadyExists + + err_val = face.NetworkError(None, None, + interfaces.StatusCode.ALREADY_EXISTS, None) + self._create_table_error_helper(err_val, AlreadyExists) + + @unittest2.skipUnless(sys.version_info[:2] == (2, 7), + 'gRPC only in Python 2.7') + def test_create_table_connection_error(self): + from grpc.beta import interfaces + from grpc.framework.interfaces.face import face + err_val = face.NetworkError(None, None, + interfaces.StatusCode.INTERNAL, None) + self._create_table_error_helper(err_val, face.NetworkError) + + @unittest2.skipUnless(sys.version_info[:2] == (2, 7), + 'gRPC only in Python 2.7') + def test_create_table_other_error(self): + self._create_table_error_helper(RuntimeError, RuntimeError) + def _delete_table_helper(self, disable=False): from gcloud._testing import _Monkey from gcloud.bigtable.happybase import connection as MUT @@ -612,6 +663,7 @@ class _MockLowLevelTable(object): def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs + self.create_error = kwargs.get('create_error') self.delete_calls = 0 self.create_calls = 0 self.col_fam_created = [] @@ -621,6 +673,8 @@ def delete(self): def create(self): self.create_calls += 1 + if self.create_error: + raise self.create_error def column_family(self, column_family_id, gc_rule=None): result = _MockLowLevelColumnFamily(column_family_id, gc_rule=gc_rule)