Skip to content

Commit

Permalink
Updating _DatastoreAPIOverGRPC to create an insecure stub.
Browse files Browse the repository at this point in the history
This was done since the local emulator doesn't use a secure
connection. In the process, also

- Using the DATASTORE_EMULATOR_HOST env. var. instead of
  DATASTORE_HOST since the former is just the host without
  the protocol (we do this to avoid having to parse anything)
- Making "port" optional in make_insecure_stub() since the
  env. var. provided by the datastore emulator already has
  the port in it
- Removing the "/datastore/" path from the GCD emulator
  URI (no longer needed)
  • Loading branch information
dhermes committed Sep 7, 2016
1 parent 98db20b commit 1e22d07
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 32 deletions.
14 changes: 9 additions & 5 deletions google/cloud/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ def make_secure_stub(credentials, user_agent, stub_class, host):
return stub_class(channel)


def make_insecure_stub(stub_class, host, port):
def make_insecure_stub(stub_class, host, port=None):
"""Makes an insecure stub for an RPC service.
Uses / depends on gRPC.
Expand All @@ -655,16 +655,20 @@ def make_insecure_stub(stub_class, host, port):
:param stub_class: A gRPC stub type for a given service.
:type host: str
:param host: The host for the service.
:param host: The host for the service. May also include the port
if ``port`` is unspecified.
:type port: int
:param port: The port for the service.
:param port: (Optional) The port for the service.
:rtype: object, instance of ``stub_class``
:returns: The stub object used to make gRPC requests to a given API.
"""
# NOTE: This assumes port != http_client.HTTPS_PORT:
target = '%s:%d' % (host, port)
if port is None:
target = host
else:
# NOTE: This assumes port != http_client.HTTPS_PORT:
target = '%s:%d' % (host, port)
channel = grpc.insecure_channel(target)
return stub_class(channel)

Expand Down
31 changes: 20 additions & 11 deletions google/cloud/datastore/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from google.rpc import status_pb2

from google.cloud._helpers import make_insecure_stub
from google.cloud._helpers import make_secure_stub
from google.cloud import connection as connection_module
from google.cloud.environment_vars import DISABLE_GRPC
Expand Down Expand Up @@ -232,13 +233,20 @@ class _DatastoreAPIOverGRPC(object):
:type connection: :class:`google.cloud.datastore.connection.Connection`
:param connection: A connection object that contains helpful
information for making requests.
:type secure: bool
:param secure: Flag indicating if a secure stub connection is needed.
"""

def __init__(self, connection):
self._stub = make_secure_stub(connection.credentials,
connection.USER_AGENT,
datastore_grpc_pb2.DatastoreStub,
DATASTORE_API_HOST)
def __init__(self, connection, secure):
if secure:
self._stub = make_secure_stub(connection.credentials,
connection.USER_AGENT,
datastore_grpc_pb2.DatastoreStub,
connection.host)
else:
self._stub = make_insecure_stub(datastore_grpc_pb2.DatastoreStub,
connection.host)

def lookup(self, project, request_pb):
"""Perform a ``lookup`` request.
Expand Down Expand Up @@ -373,14 +381,15 @@ class Connection(connection_module.Connection):
def __init__(self, credentials=None, http=None):
super(Connection, self).__init__(credentials=credentials, http=http)
try:
# gcd.sh has /datastore/ in the path still since it supports
# v1beta2 and v1beta3 simultaneously.
api_base_url = '%s/datastore' % (os.environ[GCD_HOST],)
self.host = os.environ[GCD_HOST]
self.api_base_url = 'http://' + self.host
secure = False
except KeyError:
api_base_url = self.__class__.API_BASE_URL
self.api_base_url = api_base_url
self.host = DATASTORE_API_HOST
self.api_base_url = self.__class__.API_BASE_URL
secure = True
if _USE_GRPC:
self._datastore_api = _DatastoreAPIOverGRPC(self)
self._datastore_api = _DatastoreAPIOverGRPC(self, secure=secure)
else:
self._datastore_api = _DatastoreAPIOverHttp(self)

Expand Down
2 changes: 1 addition & 1 deletion google/cloud/environment_vars.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
GCD_DATASET = 'DATASTORE_DATASET'
"""Environment variable defining default dataset ID under GCD."""

GCD_HOST = 'DATASTORE_HOST'
GCD_HOST = 'DATASTORE_EMULATOR_HOST'
"""Environment variable defining host for GCD dataset server."""

PUBSUB_EMULATOR = 'PUBSUB_EMULATOR_HOST'
Expand Down
46 changes: 36 additions & 10 deletions unit_tests/datastore/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,29 +112,35 @@ def _getTargetClass(self):
from google.cloud.datastore.connection import _DatastoreAPIOverGRPC
return _DatastoreAPIOverGRPC

def _makeOne(self, stub, connection=None, mock_args=None):
def _makeOne(self, stub, connection=None, secure=True, mock_args=None):
from unit_tests._testing import _Monkey
from google.cloud.datastore import connection as MUT

if connection is None:
connection = _Connection(None)
connection.credentials = object()
connection.host = 'CURR_HOST'

if mock_args is None:
mock_args = []

def mock_make_secure_stub(*args):
def mock_make_stub(*args):
mock_args.append(args)
return stub

with _Monkey(MUT, make_secure_stub=mock_make_secure_stub):
return self._getTargetClass()(connection)
if secure:
to_monkey = {'make_secure_stub': mock_make_stub}
else:
to_monkey = {'make_insecure_stub': mock_make_stub}
with _Monkey(MUT, **to_monkey):
return self._getTargetClass()(connection, secure)

def test_constructor(self):
from google.cloud.datastore import connection as MUT

conn = _Connection(None)
conn.credentials = object()
conn.host = 'CURR_HOST'

stub = _GRPCStub()
mock_args = []
Expand All @@ -146,7 +152,26 @@ def test_constructor(self):
conn.credentials,
conn.USER_AGENT,
MUT.datastore_grpc_pb2.DatastoreStub,
MUT.DATASTORE_API_HOST,
conn.host,
)])

def test_constructor_insecure(self):
from gcloud.datastore import connection as MUT

conn = _Connection(None)
conn.credentials = object()
conn.host = 'CURR_HOST:1234'

stub = _GRPCStub()
mock_args = []
datastore_api = self._makeOne(stub, connection=conn,
secure=False,
mock_args=mock_args)
self.assertIs(datastore_api._stub, stub)

self.assertEqual(mock_args, [(
MUT.datastore_grpc_pb2.DatastoreStub,
conn.host,
)])

def test_lookup(self):
Expand Down Expand Up @@ -322,7 +347,7 @@ def test_custom_url_from_env(self):
conn = self._makeOne()

self.assertNotEqual(conn.api_base_url, API_BASE_URL)
self.assertEqual(conn.api_base_url, HOST + '/datastore')
self.assertEqual(conn.api_base_url, 'http://' + HOST)

def test_ctor_defaults(self):
conn = self._makeOne()
Expand Down Expand Up @@ -350,19 +375,19 @@ def test_ctor_with_grpc(self):
from unit_tests._testing import _Monkey
from google.cloud.datastore import connection as MUT

connections = []
api_args = []
return_val = object()

def mock_api(connection):
connections.append(connection)
def mock_api(connection, secure):
api_args.append((connection, secure))
return return_val

with _Monkey(MUT, _DatastoreAPIOverGRPC=mock_api):
conn = self._makeOne(use_grpc=True)

self.assertEqual(conn.credentials, None)
self.assertIs(conn._datastore_api, return_val)
self.assertEqual(connections, [conn])
self.assertEqual(api_args, [(conn, True)])

def test_ctor_explicit(self):
class Creds(object):
Expand Down Expand Up @@ -1065,6 +1090,7 @@ def request(self, **kw):

class _Connection(object):

host = None
USER_AGENT = 'you-sir-age-int'

def __init__(self, api_url):
Expand Down
17 changes: 12 additions & 5 deletions unit_tests/test__helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -975,7 +975,7 @@ def _callFUT(self, *args, **kwargs):
from gcloud._helpers import make_insecure_stub
return make_insecure_stub(*args, **kwargs)

def test_it(self):
def _helper(self, target, host, port=None):
from unit_tests._testing import _Monkey
from gcloud import _helpers as MUT

Expand All @@ -995,16 +995,23 @@ def mock_stub_class(channel):
stub_inputs.append(channel)
return mock_result

host = 'HOST'
port = 1025
with _Monkey(MUT, grpc=grpc_mod):
result = self._callFUT(mock_stub_class, host, port)
result = self._callFUT(mock_stub_class, host, port=port)

self.assertTrue(result is mock_result)
self.assertEqual(stub_inputs, [CHANNEL])
target = '%s:%d' % (host, port)
self.assertEqual(grpc_mod.insecure_channel_args, (target,))

def test_with_port_argument(self):
host = 'HOST'
port = 1025
target = '%s:%d' % (host, port)
self._helper(target, host, port=port)

def test_without_port_argument(self):
host = 'HOST:1114'
self._helper(host, host)


class Test_exc_to_code(unittest.TestCase):

Expand Down

0 comments on commit 1e22d07

Please sign in to comment.