Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: supply anonymous credentials under emulator #71

Merged
merged 2 commits into from Aug 14, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering why we don't use the DATASTORE_DATASET defined above here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point!


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