Skip to content

Commit

Permalink
feat: supply anonymous credentials under emulator
Browse files Browse the repository at this point in the history
See: #70.
  • Loading branch information
tseaver committed Aug 13, 2020
1 parent f3283e1 commit 0d2e75e
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 27 deletions.
31 changes: 23 additions & 8 deletions google/cloud/datastore/client.py
Expand Up @@ -16,6 +16,7 @@
import os

import google.api_core.client_options
from google.auth.credentials import AnonymousCredentials
from google.cloud._helpers import _LocalStack
from google.cloud._helpers import _determine_default_project as _base_default_project
from google.cloud.client import ClientWithProject
Expand All @@ -27,9 +28,6 @@
from google.cloud.datastore.key import Key
from google.cloud.datastore.query import Query
from google.cloud.datastore.transaction import Transaction
from google.cloud.environment_vars import DISABLE_GRPC
from google.cloud.environment_vars import GCD_DATASET
from google.cloud.environment_vars import GCD_HOST

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

DATASTORE_EMULATOR_HOST = "DATASTORE_EMULATOR_HOST"
"""Environment variable defining host for datastore emulator server."""
DATASTORE_DATASET = "DATASTORE_DATASET"
"""Environment variable defining default dataset ID under GCD."""
DISABLE_GRPC = "GOOGLE_CLOUD_DISABLE_GRPC"
"""Environment variable acting as flag to disable gRPC."""


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


def _get_gcd_project():
"""Gets the GCD application ID if it can be inferred."""
return os.getenv(GCD_DATASET)
return os.getenv(DATASTORE_DATASET)


def _determine_default_project(project=None):
Expand Down Expand Up @@ -266,6 +271,15 @@ def __init__(
_http=None,
_use_grpc=None,
):
emulator_host = os.getenv(DATASTORE_EMULATOR_HOST)

if emulator_host is not None:
if credentials is not None:
raise ValueError(
"Explicit credentials are incompatible with the emulator"
)
credentials = AnonymousCredentials()

super(Client, self).__init__(
project=project,
credentials=credentials,
Expand All @@ -277,14 +291,15 @@ def __init__(
self._client_options = client_options
self._batch_stack = _LocalStack()
self._datastore_api_internal = None

if _use_grpc is None:
self._use_grpc = _USE_GRPC
else:
self._use_grpc = _use_grpc
try:
host = os.environ[GCD_HOST]
self._base_url = "http://" + host
except KeyError:

if emulator_host is not None:
self._base_url = "http://" + emulator_host
else:
api_endpoint = _DATASTORE_BASE_URL
if client_options:
if type(client_options) == dict:
Expand Down
26 changes: 14 additions & 12 deletions tests/system/test_system.py
Expand Up @@ -25,7 +25,6 @@
from google.cloud.environment_vars import GCD_DATASET
from google.cloud.exceptions import Conflict

from test_utils.system import EmulatorCreds
from test_utils.system import unique_resource_id

from tests.system.utils import clear_datastore
Expand All @@ -44,12 +43,19 @@ class Config(object):


def clone_client(client):
return datastore.Client(
project=client.project,
namespace=client.namespace,
credentials=client._credentials,
_http=client._http,
)
emulator_dataset = os.getenv(GCD_DATASET)

if emulator_dataset is None:
return datastore.Client(
project=client.project,
namespace=client.namespace,
credentials=client._credentials,
_http=client._http,
)
else:
return datastore.Client(
project=client.project, namespace=client.namespace, _http=client._http,
)


def setUpModule():
Expand All @@ -59,13 +65,9 @@ def setUpModule():
if emulator_dataset is None:
Config.CLIENT = datastore.Client(namespace=test_namespace)
else:
credentials = EmulatorCreds()
http = requests.Session() # Un-authorized.
Config.CLIENT = datastore.Client(
project=emulator_dataset,
namespace=test_namespace,
credentials=credentials,
_http=http,
project=emulator_dataset, namespace=test_namespace, _http=http,
)


Expand Down
29 changes: 22 additions & 7 deletions tests/unit/test_client.py
Expand Up @@ -52,10 +52,10 @@ def test_no_value(self):
self.assertIsNone(project)

def test_value_set(self):
from google.cloud.datastore.client import GCD_DATASET
from google.cloud.datastore.client import DATASTORE_DATASET

MOCK_PROJECT = object()
environ = {GCD_DATASET: MOCK_PROJECT}
environ = {DATASTORE_DATASET: MOCK_PROJECT}
with mock.patch("os.getenv", new=environ.get):
project = self._call_fut()
self.assertEqual(project, MOCK_PROJECT)
Expand Down Expand Up @@ -235,18 +235,33 @@ def test_constructor_use_grpc_default(self):
)
self.assertTrue(client4._use_grpc)

def test_constructor_gcd_host(self):
from google.cloud.environment_vars import GCD_HOST
def test_constructor_w_emulator_w_creds(self):
from google.cloud.datastore.client import DATASTORE_EMULATOR_HOST

host = "localhost:1234"
fake_environ = {GCD_HOST: host}
fake_environ = {DATASTORE_EMULATOR_HOST: host}
project = "PROJECT"
creds = _make_credentials()
http = object()

with mock.patch("os.environ", new=fake_environ):
client = self._make_one(project=project, credentials=creds, _http=http)
self.assertEqual(client.base_url, "http://" + host)
with self.assertRaises(ValueError):
self._make_one(project=project, credentials=creds, _http=http)

def test_constructor_w_emulator_wo_creds(self):
from google.auth.credentials import AnonymousCredentials
from google.cloud.datastore.client import DATASTORE_EMULATOR_HOST

host = "localhost:1234"
fake_environ = {DATASTORE_EMULATOR_HOST: host}
project = "PROJECT"
http = object()

with mock.patch("os.environ", new=fake_environ):
client = self._make_one(project=project, _http=http)

self.assertEqual(client.base_url, "http://" + host)
self.assertIsInstance(client._credentials, AnonymousCredentials)

def test_base_url_property(self):
from google.cloud.datastore.client import _DATASTORE_BASE_URL
Expand Down

0 comments on commit 0d2e75e

Please sign in to comment.