8000 feat: supply anonymous credentials under emulator (#71) · samkenxstream/python-datastore@4db3c40 · GitHub
[go: up one dir, main page]

8000
Skip to content

Commit 4db3c40

Browse files
authored
feat: supply anonymous credentials under emulator (googleapis#71)
Closes: googleapis#70.
1 parent f3283e1 commit 4db3c40

File tree

3 files changed

+64
-32
lines changed

3 files changed

+64
-32
lines changed

google/cloud/datastore/client.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import os
1717

1818
import google.api_core.client_options
19+
from google.auth.credentials import AnonymousCredentials
1920
from google.cloud._helpers import _LocalStack
2021
from google.cloud._helpers import _determine_default_project as _base_default_project
2122
from google.cloud.client import ClientWithProject
@@ -27,9 +28,6 @@
2728
from google.cloud.datastore.key import Key
2829
from google.cloud.datastore.query import Query
2930
from google.cloud.datastore.transaction import Transaction
30-
from google.cloud.environment_vars import DISABLE_GRPC
31-
from google.cloud.environment_vars import GCD_DATASET
32-
from google.cloud.environment_vars import GCD_HOST
3331

3432
try:
3533
from google.cloud.datastore._gapic import make_datastore_api
@@ -54,13 +52,20 @@
5452
_DATASTORE_BASE_URL = "https://datastore.googleapis.com"
5553
"""Datastore API request URL base."""
5654

55+
DATASTORE_EMULATOR_HOST = "DATASTORE_EMULATOR_HOST"
56+
"""Environment variable defining host for datastore emulator server."""
57+
DATASTORE_DATASET = "DATASTORE_DATASET"
58+
"""Environment variable defining default dataset ID under GCD."""
59+
DISABLE_GRPC = "GOOGLE_CLOUD_DISABLE_GRPC"
60+
"""Environment variable acting as flag to disable gRPC."""
61+
5762

5863
_USE_GRPC = _HAVE_GRPC and not os.getenv(DISABLE_GRPC, False)
5964

6065

6166
def _get_gcd_project():
6267
"""Gets the GCD application ID if it can be inferred."""
63-
return os.getenv(GCD_DATASET)
68+
return os.getenv(DATASTORE_DATASET)
6469

6570

6671
def _determine_default_project(project=None):
@@ -266,6 +271,15 @@ def __init__(
266271
_http=None,
267272
_use_grpc=None,
268273
):
274+
emulator_host = os.getenv(DATASTORE_EMULATOR_HOST)
275+
276+
if emulator_host is not None:
277+
if credentials is not None:
278+
raise ValueError(
279+
"Explicit credentials are incompatible with the emulator"
280+
)
281+
credentials = AnonymousCredentials()
282+
269283
super(Client, self).__init__(
270284
project=project,
271285
credentials=credentials,
@@ -277,14 +291,15 @@ def __init__(
277291
self._client_options = client_options
278292
self._batch_stack = _LocalStack()
279293
self._datastore_api_internal = None
294+
280295
if _use_grpc is None:
281296
self._use_grpc = _USE_GRPC
282297
else:
283298
self._use_grpc = _use_grpc
284-
try:
285-
host = os.environ[GCD_HOST]
286-
self._base_url = "http://" + host
287-
except KeyError:
299+
300+
if emulator_host is not None:
301+
self._base_url = "http://" + emulator_host
302+
else:
288303
api_endpoint = _DATASTORE_BASE_URL
289304
if client_options:
290305
if type(client_options) == dict:

tests/system/test_system.py

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,9 @@
2222
from google.cloud._helpers import UTC
2323
from google.cloud import datastore
2424
from google.cloud.datastore.helpers import GeoPoint
25-
from google.cloud.environment_vars import GCD_DATASET
25+
from google.cloud.datastore.client import DATASTORE_DATASET
2626
from google.cloud.exceptions import Conflict
2727

28-
from test_utils.system import EmulatorCreds
2928
from test_utils.system import unique_resource_id
3029

3130
from tests.system.utils import clear_datastore
@@ -44,28 +43,31 @@ class Config(object):
4443

4544

4645
def clone_client(client):
47-
return datastore.Client(
48-
project=client.project,
49-
namespace=client.namespace,
50-
credentials=client._credentials,
51-
_http=client._http,
52-
)
46+
emulator_dataset = os.getenv(DATASTORE_DATASET)
47+
48+
if emulator_dataset is None:
49+
return datastore.Client(
50+
project=client.project,
51+
namespace=client.namespace,
52+
credentials=client._credentials,
53+
_http=client._http,
54+
)
55+
else:
56+
return datastore.Client(
57+
project=client.project, namespace=client.namespace, _http=client._http,
58+
)
5359

5460

5561
def setUpModule():
56-
emulator_dataset = os.getenv(GCD_DATASET)
62+
emulator_dataset = os.getenv(DATASTORE_DATASET)
5763
# Isolated namespace so concurrent test runs don't collide.
5864
test_namespace = "ns" + unique_resource_id()
5965
if emulator_dataset is None:
6066
Config.CLIENT = datastore.Client(namespace=test_namespace)
6167
else:
62-
credentials = EmulatorCreds()
6368
http = requests.Session() # Un-authorized.
6469
Config.CLIENT = datastore.Client(
65-
project=emulator_dataset,
66-
namespace=test_namespace,
67-
credentials=credentials,
68-
_http=http,
70+
project=emulator_dataset, namespace=test_namespace, _http=http,
6971
)
7072

7173

@@ -240,7 +242,7 @@ def setUpClass(cls):
240242
cls.CLIENT.namespace = None
241243

242244
# In the emulator, re-populating the datastore is cheap.
243-
if os.getenv(GCD_DATASET) is not None:
245+
if os.getenv(DATASTORE_DATASET) is not None:
244246
# Populate the datastore with the cloned client.
245247
populate_datastore.add_characters(client=cls.CLIENT)
246248

@@ -251,7 +253,7 @@ def setUpClass(cls):
251253
@classmethod
252254
def tearDownClass(cls):
253255
# In the emulator, destroy the query entities.
254-
if os.getenv(GCD_DATASET) is not None:
256+
if os.getenv(DATASTORE_DATASET) is not None:
255257
# Use the client for this test instead of the global.
256258
clear_datastore.remove_all_entities(client=cls.CLIENT)
257259

@@ -484,7 +486,7 @@ def setUpClass(cls):
484486
@classmethod
485487
def tearDownClass(cls):
486488
# In the emulator, destroy the query entities.
487-
if os.getenv(GCD_DATASET) is not None:
489+
if os.getenv(DATASTORE_DATASET) is not None:
488490
# Use the client for this test instead of the global.
489491
clear_datastore.remove_all_entities(client=cls.CLIENT)
490492

tests/unit/test_client.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ def test_no_value(self):
5252
self.assertIsNone(project)
5353

5454
def test_value_set(self):
55-
from google.cloud.datastore.client import GCD_DATASET
55+
from google.cloud.datastore.client import DATASTORE_DATASET
5656

5757
MOCK_PROJECT = object()
58-
environ = {GCD_DATASET: MOCK_PROJECT}
58+
environ = {DATASTORE_DATASET: MOCK_PROJECT}
5959
with mock.patch("os.getenv", new=environ.get):
6060
project = self._call_fut()
6161
self.assertEqual(project, MOCK_PROJECT)
@@ -235,18 +235,33 @@ def test_constructor_use_grpc_default(self):
235235
)
236236
self.assertTrue(client4._use_grpc)
237237

238-
def test_constructor_gcd_host(self):
239-
from google.cloud.environment_vars import GCD_HOST
238+
def test_constructor_w_emulator_w_creds(self):
239+
from google.cloud.datastore.client import DATASTORE_EMULATOR_HOST
240240

241241
host = "localhost:1234"
242-
fake_environ = {GCD_HOST: host}
242+
fake_environ = {DATASTORE_EMULATOR_HOST: host}
243243
project = "PROJECT"
244244
creds = _make_credentials()
245245
http = object()
246246

247247
with mock.patch("os.environ", new=fake_environ):
248-
client = self._make_one(project=project, credentials=creds, _http=http)
249-
self.assertEqual(client.base_url, "http://" + host)
248+
with self.assertRaises(ValueError):
249+
self._make_one(project=project, credentials=creds, _http=http)
250+
251+
def test_constructor_w_emulator_wo_creds(self):
252+
from google.auth.credentials import AnonymousCredentials
253+
from google.cloud.datastore.client import DATASTORE_EMULATOR_HOST
254+
255+
host = "localhost:1234"
256+
fake_environ = {DATASTORE_EMULATOR_HOST: host}
257+
project = "PROJECT"
258+
http = object()
259+
260+
with mock.patch("os.environ", new=fake_environ):
261+
client = self._make_one(project=project, _http=http)
262+
263+
self.assertEqual(client.base_url, "http://" + host)
264+
self.assertIsInstance(client._credentials, AnonymousCredentials)
250265

251266
def test_base_url_property(self):
252267
from google.cloud.datastore.client import _DATASTORE_BASE_URL

0 commit comments

Comments
 (0)
0