Repository: googleapis/google-cloud-python

Issue:
google-cloud-bigtable 0.30.0 doesn't work with non-admin context
We are seeing an issue with the newly release google-cloud-bigtable (0.30.0):

Python version:
```
$ pipenv run python --version
Python 2.7.12
```

Package version:
```
        "google-cloud-bigtable": {
            "hashes": [
                "sha256:0b82e3c77db6ac89f9111551042146fc1d829fb67f77c809bb8465923ef0455b",
                "sha256:bd39cabfd6e816646940a10e1576d59f2e4f272b3ba0c5e040c834b949a9ba4f"
            ],
            "index": "pypi",
            "version": "==0.30.0"
        },
```

A simple repro script:

```
#!/usr/bin/env python

import sys

from google.cloud import bigtable

proj, inst, tbl = sys.argv[1:]

thandle = bigtable.Client(project=proj, admin=False).instance(inst).table(tbl)
row = thandle.read_row('some_arbitrary_key')(venv)
```

With 0.30.0, this fails:

```
$ pipenv run python ./repro.py <project> <instance> <table>
/Users/andy/.local/share/virtualenvs/btscene-9KYRlr-O/lib/python2.7/site-packages/google/auth/_default.py:66: UserWarning: Your application has authenticated using end user credentials from Google Cloud SDK. We recommend that most server applications use service accounts instead. If your application continues to use end user credentials from Cloud SDK, you might receive a "quota exceeded" or "API not enabled" error. For more information about service accounts, see https://cloud.google.com/docs/authentication/.
  warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING)
Traceback (most recent call last):
  File "./repro.py", line 10, in <module>
    row = thandle.read_row('some_arbitrary_key')
  File "/Users/andy/.local/share/virtualenvs/btscene-9KYRlr-O/lib/python2.7/site-packages/google/cloud/bigtable/table.py", line 292, in read_row
    self.name, row_key=row_key, filter_=filter_,
  File "/Users/andy/.local/share/virtualenvs/btscene-9KYRlr-O/lib/python2.7/site-packages/google/cloud/bigtable/table.py", line 125, in name
    table_client = self._instance._client.table_admin_client
  File "/Users/andy/.local/share/virtualenvs/btscene-9KYRlr-O/lib/python2.7/site-packages/google/cloud/bigtable/client.py", line 179, in table_admin_client
    raise ValueError('Client is not an admin client.')
ValueError: Client is not an admin client.
```

After downgrading to 0.29.0, we receive the expected output:

```
$ pipenv run python ./repro.py <project> <instance> <table>
/Users/andy/.local/share/virtualenvs/btscene-9KYRlr-O/lib/python2.7/site-packages/google/auth/_default.py:66: UserWarning: Your application has authenticated using end user credentials from Google Cloud SDK. We recommend that most server applications use service accounts instead. If your application continues to use end user credentials from Cloud SDK, you might receive a "quota exceeded" or "API not enabled" error. For more information about service accounts, see https://cloud.google.com/docs/authentication/.
  warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING)
```
(there was no row with that key on our table)


Discussion:
@andyhky Thanks for the report!  I can reproduce, and am working on a fix right now.

Resolving patch:
diff --git a/bigtable/google/cloud/bigtable/client.py b/bigtable/google/cloud/bigtable/client.py
--- a/bigtable/google/cloud/bigtable/client.py
+++ b/bigtable/google/cloud/bigtable/client.py
@@ -156,8 +156,6 @@ def table_data_client(self):
         :returns: A BigtableClient object.
         """
         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))
diff --git a/bigtable/google/cloud/bigtable/table.py b/bigtable/google/cloud/bigtable/table.py
--- a/bigtable/google/cloud/bigtable/table.py
+++ b/bigtable/google/cloud/bigtable/table.py
@@ -123,7 +123,7 @@ def name(self):
         """
         project = self._instance._client.project
         instance_id = self._instance.instance_id
-        table_client = self._instance._client.table_admin_client
+        table_client = self._instance._client.table_data_client
         return table_client.table_path(
             project=project, instance=instance_id, table=self.table_id)
 


Test patch:
diff --git a/bigtable/tests/system.py b/bigtable/tests/system.py
--- a/bigtable/tests/system.py
+++ b/bigtable/tests/system.py
@@ -1013,3 +1013,9 @@ def test_read_with_label_applied(self):
         self.assertEqual(cell3_new.timestamp, cell3.timestamp)
         self.assertEqual(cell3.labels, [])
         self.assertEqual(cell3_new.labels, [label2])
+
+    def test_access_with_non_admin_client(self):
+        client = Client(admin=False)
+        instance = client.instance(INSTANCE_ID)
+        table = instance.table(self._table.table_id)
+        self.assertIsNone(table.read_row('nonesuch'))
diff --git a/bigtable/tests/unit/test_client.py b/bigtable/tests/unit/test_client.py
--- a/bigtable/tests/unit/test_client.py
+++ b/bigtable/tests/unit/test_client.py
@@ -125,46 +125,74 @@ def test_instance_factory_non_defaults(self):
         self.assertEqual(instance.labels, labels)
         self.assertIs(instance._client, client)
 
-    def test_admin_client_w_value_error(self):
+    def test_table_data_client_not_initialized(self):
+        from google.cloud.bigtable_v2 import BigtableClient
+
         credentials = _make_credentials()
         client = self._make_one(project=self.PROJECT, credentials=credentials)
 
-        with self.assertRaises(ValueError):
-            client.table_admin_client()
-
-        with self.assertRaises(ValueError):
-            client.instance_admin_client()
+        table_data_client = client.table_data_client
+        self.assertIsInstance(table_data_client, BigtableClient)
+        self.assertIs(client._table_data_client, table_data_client)
 
-    def test_table_data_client(self):
+    def test_table_data_client_initialized(self):
         credentials = _make_credentials()
         client = self._make_one(project=self.PROJECT, credentials=credentials,
                                 admin=True)
 
-        table_data_client = client.table_data_client
-        self.assertEqual(client._table_data_client, table_data_client)
+        already = client._table_data_client = object()
+        self.assertIs(client.table_data_client, already)
 
-        client._table_data_client = object()
-        table_data_client = client.table_data_client
-        self.assertEqual(client.table_data_client, table_data_client)
+    def test_table_admin_client_not_initialized_no_admin_flag(self):
+        credentials = _make_credentials()
+        client = self._make_one(project=self.PROJECT, credentials=credentials)
+
+        with self.assertRaises(ValueError):
+            client.table_admin_client()
+
+    def test_table_admin_client_not_initialized_w_admin_flag(self):
+        from google.cloud.bigtable_admin_v2 import BigtableTableAdminClient
 
-    def test_table_admin_client(self):
         credentials = _make_credentials()
-        client = self._make_one(project=self.PROJECT, credentials=credentials,
-                                admin=True)
+        client = self._make_one(
+            project=self.PROJECT, credentials=credentials, admin=True)
 
         table_admin_client = client.table_admin_client
-        self.assertEqual(client._table_admin_client, table_admin_client)
+        self.assertIsInstance(table_admin_client, BigtableTableAdminClient)
 
-        client._table_admin_client = object()
-        table_admin_client = client.table_admin_client
-        self.assertEqual(client._table_admin_client, table_admin_client)
+    def test_table_admin_client_initialized(self):
+        credentials = _make_credentials()
+        client = self._make_one(project=self.PROJECT, credentials=credentials,
+                                admin=True)
 
-    def test_table_data_client_w_value_error(self):
+        already = client._table_admin_client = object()
+        self.assertIs(client.table_admin_client, already)
+
+    def test_instance_admin_client_not_initialized_no_admin_flag(self):
         credentials = _make_credentials()
         client = self._make_one(project=self.PROJECT, credentials=credentials)
 
         with self.assertRaises(ValueError):
-            client.table_data_client()
+            client.instance_admin_client()
+
+    def test_instance_admin_client_not_initialized_w_admin_flag(self):
+        from google.cloud.bigtable_admin_v2 import BigtableInstanceAdminClient
+
+        credentials = _make_credentials()
+        client = self._make_one(
+            project=self.PROJECT, credentials=credentials, admin=True)
+
+        instance_admin_client = client.instance_admin_client
+        self.assertIsInstance(
+            instance_admin_client, BigtableInstanceAdminClient)
+
+    def test_instance_admin_client_initialized(self):
+        credentials = _make_credentials()
+        client = self._make_one(project=self.PROJECT, credentials=credentials,
+                                admin=True)
+
+        already = client._instance_admin_client = object()
+        self.assertIs(client.instance_admin_client, already)
 
     def test_list_instances(self):
         from google.cloud.bigtable_admin_v2.proto import (
diff --git a/bigtable/tests/unit/test_table.py b/bigtable/tests/unit/test_table.py
--- a/bigtable/tests/unit/test_table.py
+++ b/bigtable/tests/unit/test_table.py
@@ -156,15 +156,25 @@ def _get_target_client_class():
     def _make_client(self, *args, **kwargs):
         return self._get_target_client_class()(*args, **kwargs)
 
-    def test_constructor(self):
+    def test_constructor_w_admin(self):
         credentials = _make_credentials()
-        client = self._make_client(project='project-id',
+        client = self._make_client(project=self.PROJECT_ID,
                                    credentials=credentials, admin=True)
-        table_id = 'table-id'
         instance = client.instance(instance_id=self.INSTANCE_ID)
         table = self._make_one(self.TABLE_ID, instance)
-        self.assertEqual(table.table_id, table_id)
+        self.assertEqual(table.table_id, self.TABLE_ID)
+        self.assertIs(table._instance._client, client)
+        self.assertEqual(table.name, self.TABLE_NAME)
+
+    def test_constructor_wo_admin(self):
+        credentials = _make_credentials()
+        client = self._make_client(project=self.PROJECT_ID,
+                                   credentials=credentials, admin=False)
+        instance = client.instance(instance_id=self.INSTANCE_ID)
+        table = self._make_one(self.TABLE_ID, instance)
+        self.assertEqual(table.table_id, self.TABLE_ID)
         self.assertIs(table._instance._client, client)
+        self.assertEqual(table.name, self.TABLE_NAME)
 
     def test_row_factory_direct(self):
         from google.cloud.bigtable.row import DirectRow
@@ -804,8 +814,6 @@ def test_truncate(self):
         client._table_admin_client = table_api
         instance = client.instance(instance_id=self.INSTANCE_ID)
         table = self._make_one(self.TABLE_ID, instance)
-        table.name.return_value = client._table_data_client.table_path(
-            self.PROJECT_ID,  self.INSTANCE_ID, self.TABLE_ID)
 
         expected_result = None  # truncate() has no return value.
         with mock.patch('google.cloud.bigtable.table.Table.name',