feat: allow default_host and default_scopes to be passed to create_channel (#134)
Add `default_host` and `default_scopes` parameters to `create_channel` so self-signed JWTs can be used.
diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py
index 0ccbe12..5937f18 100644
--- a/google/api_core/grpc_helpers.py
+++ b/google/api_core/grpc_helpers.py
@@ -17,6 +17,8 @@
import collections
import grpc
+from packaging import version
+import pkg_resources
import six
from google.api_core import exceptions
@@ -33,6 +35,20 @@
except ImportError:
HAS_GRPC_GCP = False
+try:
+ # google.auth.__version__ was added in 1.26.0
+ _GOOGLE_AUTH_VERSION = google.auth.__version__
+except AttributeError:
+ try: # try pkg_resources if it is available
+ _GOOGLE_AUTH_VERSION = pkg_resources.get_distribution("google-auth").version
+ except pkg_resources.DistributionNotFound: # pragma: NO COVER
+ _GOOGLE_AUTH_VERSION = None
+
+if _GOOGLE_AUTH_VERSION is not None and version.parse(_GOOGLE_AUTH_VERSION) >= version.parse("1.25.0"):
+ _GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST = True
+else:
+ _GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST = False
+
# The list of gRPC Callable interfaces that return iterators.
_STREAM_WRAP_CLASSES = (grpc.UnaryStreamMultiCallable, grpc.StreamStreamMultiCallable)
@@ -179,9 +195,11 @@
def _create_composite_credentials(
credentials=None,
credentials_file=None,
+ default_scopes=None,
scopes=None,
ssl_credentials=None,
- quota_project_id=None):
+ quota_project_id=None,
+ default_host=None):
"""Create the composite credentials for secure channels.
Args:
@@ -191,12 +209,16 @@
credentials_file (str): A file with credentials that can be loaded with
:func:`google.auth.load_credentials_from_file`. This argument is
mutually exclusive with credentials.
+ default_scopes (Sequence[str]): A optional list of scopes needed for this
+ service. These are only used when credentials are not specified and
+ are passed to :func:`google.auth.default`.
scopes (Sequence[str]): A optional list of scopes needed for this
service. These are only used when credentials are not specified and
are passed to :func:`google.auth.default`.
ssl_credentials (grpc.ChannelCredentials): Optional SSL channel
credentials. This can be used to specify different certificates.
quota_project_id (str): An optional project to use for billing and quota.
+ default_host (str): The default endpoint. e.g., "pubsub.googleapis.com".
Returns:
grpc.ChannelCredentials: The composed channel credentials object.
@@ -210,11 +232,38 @@
)
if credentials_file:
- credentials, _ = google.auth.load_credentials_from_file(credentials_file, scopes=scopes)
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if _GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ credentials, _ = google.auth.load_credentials_from_file(
+ credentials_file,
+ scopes=scopes,
+ default_scopes=default_scopes
+ )
+ else:
+ credentials, _ = google.auth.load_credentials_from_file(
+ credentials_file,
+ scopes=scopes or default_scopes,
+ )
elif credentials:
- credentials = google.auth.credentials.with_scopes_if_required(credentials, scopes)
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if _GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ credentials = google.auth.credentials.with_scopes_if_required(
+ credentials,
+ scopes=scopes,
+ default_scopes=default_scopes
+ )
+ else:
+ credentials = google.auth.credentials.with_scopes_if_required(
+ credentials,
+ scopes=scopes or default_scopes,
+ )
+
else:
- credentials, _ = google.auth.default(scopes=scopes)
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if _GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ credentials, _ = google.auth.default(scopes=scopes, default_scopes=default_scopes)
+ else:
+ credentials, _ = google.auth.default(scopes=scopes or default_scopes)
if quota_project_id and isinstance(credentials, google.auth.credentials.CredentialsWithQuotaProject):
credentials = credentials.with_quota_project(quota_project_id)
@@ -222,9 +271,16 @@
request = google.auth.transport.requests.Request()
# Create the metadata plugin for inserting the authorization header.
- metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin(
- credentials, request
- )
+
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if _GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin(
+ credentials, request, default_host=default_host,
+ )
+ else:
+ metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin(
+ credentials, request
+ )
# Create a set of grpc.CallCredentials using the metadata plugin.
google_auth_credentials = grpc.metadata_call_credentials(metadata_plugin)
@@ -245,6 +301,8 @@
ssl_credentials=None,
credentials_file=None,
quota_project_id=None,
+ default_scopes=None,
+ default_host=None,
**kwargs):
"""Create a secure channel with credentials.
@@ -262,6 +320,9 @@
:func:`google.auth.load_credentials_from_file`. This argument is
mutually exclusive with credentials.
quota_project_id (str): An optional project to use for billing and quota.
+ default_scopes (Sequence[str]): Default scopes passed by a Google client
+ library. Use 'scopes' for user-defined scopes.
+ default_host (str): The default endpoint. e.g., "pubsub.googleapis.com".
kwargs: Additional key-word args passed to
:func:`grpc_gcp.secure_channel` or :func:`grpc.secure_channel`.
@@ -275,9 +336,11 @@
composite_credentials = _create_composite_credentials(
credentials=credentials,
credentials_file=credentials_file,
+ default_scopes=default_scopes,
scopes=scopes,
ssl_credentials=ssl_credentials,
quota_project_id=quota_project_id,
+ default_host=default_host,
)
if HAS_GRPC_GCP:
diff --git a/google/api_core/grpc_helpers_async.py b/google/api_core/grpc_helpers_async.py
index 9a994e9..14eb5a1 100644
--- a/google/api_core/grpc_helpers_async.py
+++ b/google/api_core/grpc_helpers_async.py
@@ -213,6 +213,8 @@
ssl_credentials=None,
credentials_file=None,
quota_project_id=None,
+ default_scopes=None,
+ default_host=None,
**kwargs):
"""Create an AsyncIO secure channel with credentials.
@@ -230,6 +232,9 @@
:func:`google.auth.load_credentials_from_file`. This argument is
mutually exclusive with credentials.
quota_project_id (str): An optional project to use for billing and quota.
+ default_scopes (Sequence[str]): Default scopes passed by a Google client
+ library. Use 'scopes' for user-defined scopes.
+ default_host (str): The default endpoint. e.g., "pubsub.googleapis.com".
kwargs: Additional key-word args passed to :func:`aio.secure_channel`.
Returns:
@@ -243,8 +248,10 @@
credentials=credentials,
credentials_file=credentials_file,
scopes=scopes,
+ default_scopes=default_scopes,
ssl_credentials=ssl_credentials,
quota_project_id=quota_project_id,
+ default_host=default_host
)
return aio.secure_channel(target, composite_credentials, **kwargs)
diff --git a/setup.py b/setup.py
index 30adb95..5de5aaf 100644
--- a/setup.py
+++ b/setup.py
@@ -34,6 +34,7 @@
"google-auth >= 1.21.1, < 2.0dev",
"requests >= 2.18.0, < 3.0.0dev",
"setuptools >= 40.3.0",
+ "packaging >= 14.3",
"six >= 1.13.0",
"pytz",
'futures >= 3.2.0; python_version < "3.2"',
diff --git a/testing/constraints-3.6.txt b/testing/constraints-3.6.txt
index 2d49817..1fcd193 100644
--- a/testing/constraints-3.6.txt
+++ b/testing/constraints-3.6.txt
@@ -10,6 +10,7 @@
google-auth==1.21.1
requests==2.18.0
setuptools==40.3.0
+packaging==14.3
six==1.13.0
grpcio==1.29.0
grpcio-gcp==0.2.2
diff --git a/tests/asyncio/test_grpc_helpers_async.py b/tests/asyncio/test_grpc_helpers_async.py
index 766e11a..3461cbe 100644
--- a/tests/asyncio/test_grpc_helpers_async.py
+++ b/tests/asyncio/test_grpc_helpers_async.py
@@ -18,6 +18,7 @@
import pytest
from google.api_core import exceptions
+from google.api_core import grpc_helpers
from google.api_core import grpc_helpers_async
import google.auth.credentials
@@ -263,6 +264,7 @@
@mock.patch("grpc.composite_channel_credentials")
@mock.patch(
"google.auth.default",
+ autospec=True,
return_value=(mock.sentinel.credentials, mock.sentinel.projet),
)
@mock.patch("grpc.experimental.aio.secure_channel")
@@ -273,7 +275,45 @@
channel = grpc_helpers_async.create_channel(target)
assert channel is grpc_secure_channel.return_value
- default.assert_called_once_with(scopes=None)
+
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ default.assert_called_once_with(scopes=None, default_scopes=None)
+ else:
+ default.assert_called_once_with(scopes=None)
+ grpc_secure_channel.assert_called_once_with(target, composite_creds)
+
+
+@mock.patch("google.auth.transport.grpc.AuthMetadataPlugin", autospec=True)
+@mock.patch(
+ "google.auth.transport.requests.Request",
+ autospec=True,
+ return_value=mock.sentinel.Request
+)
+@mock.patch("grpc.composite_channel_credentials")
+@mock.patch(
+ "google.auth.default",
+ autospec=True,
+ return_value=(mock.sentinel.credentials, mock.sentinel.projet),
+)
+@mock.patch("grpc.experimental.aio.secure_channel")
+def test_create_channel_implicit_with_default_host(grpc_secure_channel, default, composite_creds_call, request, auth_metadata_plugin):
+ target = "example.com:443"
+ default_host = "example.com"
+ composite_creds = composite_creds_call.return_value
+
+ channel = grpc_helpers_async.create_channel(target, default_host=default_host)
+
+ assert channel is grpc_secure_channel.return_value
+
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ default.assert_called_once_with(scopes=None, default_scopes=None)
+ auth_metadata_plugin.assert_called_once_with(mock.sentinel.credentials, mock.sentinel.Request, default_host=default_host)
+ else:
+ default.assert_called_once_with(scopes=None)
+ auth_metadata_plugin.assert_called_once_with(mock.sentinel.credentials, mock.sentinel.Request)
+
grpc_secure_channel.assert_called_once_with(target, composite_creds)
@@ -292,7 +332,12 @@
grpc_helpers_async.create_channel(target, ssl_credentials=ssl_creds)
- default.assert_called_once_with(scopes=None)
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ default.assert_called_once_with(scopes=None, default_scopes=None)
+ else:
+ default.assert_called_once_with(scopes=None)
+
composite_creds_call.assert_called_once_with(ssl_creds, mock.ANY)
composite_creds = composite_creds_call.return_value
grpc_secure_channel.assert_called_once_with(target, composite_creds)
@@ -301,6 +346,7 @@
@mock.patch("grpc.composite_channel_credentials")
@mock.patch(
"google.auth.default",
+ autospec=True,
return_value=(mock.sentinel.credentials, mock.sentinel.projet),
)
@mock.patch("grpc.experimental.aio.secure_channel")
@@ -313,7 +359,39 @@
channel = grpc_helpers_async.create_channel(target, scopes=["one", "two"])
assert channel is grpc_secure_channel.return_value
- default.assert_called_once_with(scopes=["one", "two"])
+
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ default.assert_called_once_with(scopes=["one", "two"], default_scopes=None)
+ else:
+ default.assert_called_once_with(scopes=["one", "two"])
+
+ grpc_secure_channel.assert_called_once_with(target, composite_creds)
+
+
+@mock.patch("grpc.composite_channel_credentials")
+@mock.patch(
+ "google.auth.default",
+ autospec=True,
+ return_value=(mock.sentinel.credentials, mock.sentinel.projet),
+)
+@mock.patch("grpc.experimental.aio.secure_channel")
+def test_create_channel_implicit_with_default_scopes(
+ grpc_secure_channel, default, composite_creds_call
+):
+ target = "example.com:443"
+ composite_creds = composite_creds_call.return_value
+
+ channel = grpc_helpers_async.create_channel(target, default_scopes=["three", "four"])
+
+ assert channel is grpc_secure_channel.return_value
+
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ default.assert_called_once_with(scopes=None, default_scopes=["three", "four"])
+ else:
+ default.assert_called_once_with(scopes=["three", "four"])
+
grpc_secure_channel.assert_called_once_with(target, composite_creds)
@@ -331,7 +409,7 @@
@mock.patch("grpc.composite_channel_credentials")
-@mock.patch("google.auth.credentials.with_scopes_if_required")
+@mock.patch("google.auth.credentials.with_scopes_if_required", autospec=True)
@mock.patch("grpc.experimental.aio.secure_channel")
def test_create_channel_explicit(grpc_secure_channel, auth_creds, composite_creds_call):
target = "example.com:443"
@@ -339,7 +417,12 @@
channel = grpc_helpers_async.create_channel(target, credentials=mock.sentinel.credentials)
- auth_creds.assert_called_once_with(mock.sentinel.credentials, None)
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ auth_creds.assert_called_once_with(mock.sentinel.credentials, scopes=None, default_scopes=None)
+ else:
+ auth_creds.assert_called_once_with(mock.sentinel.credentials, scopes=None)
+
assert channel is grpc_secure_channel.return_value
grpc_secure_channel.assert_called_once_with(target, composite_creds)
@@ -357,8 +440,36 @@
channel = grpc_helpers_async.create_channel(
target, credentials=credentials, scopes=scopes
)
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None)
+ else:
+ credentials.with_scopes.assert_called_once_with(scopes)
- credentials.with_scopes.assert_called_once_with(scopes)
+ assert channel is grpc_secure_channel.return_value
+ grpc_secure_channel.assert_called_once_with(target, composite_creds)
+
+
+@mock.patch("grpc.composite_channel_credentials")
+@mock.patch("grpc.experimental.aio.secure_channel")
+def test_create_channel_explicit_default_scopes(grpc_secure_channel, composite_creds_call):
+ target = "example.com:443"
+ default_scopes = ["3", "4"]
+ composite_creds = composite_creds_call.return_value
+
+ credentials = mock.create_autospec(google.auth.credentials.Scoped, instance=True)
+ credentials.requires_scopes = True
+
+ channel = grpc_helpers_async.create_channel(
+ target, credentials=credentials, default_scopes=default_scopes
+ )
+
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ credentials.with_scopes.assert_called_once_with(scopes=None, default_scopes=default_scopes)
+ else:
+ credentials.with_scopes.assert_called_once_with(scopes=default_scopes)
+
assert channel is grpc_secure_channel.return_value
grpc_secure_channel.assert_called_once_with(target, composite_creds)
@@ -384,6 +495,7 @@
@mock.patch("grpc.experimental.aio.secure_channel")
@mock.patch(
"google.auth.load_credentials_from_file",
+ autospec=True,
return_value=(mock.sentinel.credentials, mock.sentinel.project)
)
def test_create_channnel_with_credentials_file(load_credentials_from_file, grpc_secure_channel, composite_creds_call):
@@ -396,7 +508,12 @@
target, credentials_file=credentials_file
)
- google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=None)
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=None, default_scopes=None)
+ else:
+ google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=None)
+
assert channel is grpc_secure_channel.return_value
grpc_secure_channel.assert_called_once_with(target, composite_creds)
@@ -405,6 +522,7 @@
@mock.patch("grpc.experimental.aio.secure_channel")
@mock.patch(
"google.auth.load_credentials_from_file",
+ autospec=True,
return_value=(mock.sentinel.credentials, mock.sentinel.project)
)
def test_create_channel_with_credentials_file_and_scopes(load_credentials_from_file, grpc_secure_channel, composite_creds_call):
@@ -418,7 +536,40 @@
target, credentials_file=credentials_file, scopes=scopes
)
- google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes)
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes, default_scopes=None)
+ else:
+ google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes)
+
+ assert channel is grpc_secure_channel.return_value
+ grpc_secure_channel.assert_called_once_with(target, composite_creds)
+
+
+@mock.patch("grpc.composite_channel_credentials")
+@mock.patch("grpc.experimental.aio.secure_channel")
+@mock.patch(
+ "google.auth.load_credentials_from_file",
+ autospec=True,
+ return_value=(mock.sentinel.credentials, mock.sentinel.project)
+)
+def test_create_channel_with_credentials_file_and_default_scopes(load_credentials_from_file, grpc_secure_channel, composite_creds_call):
+ target = "example.com:443"
+ default_scopes = ["3", "4"]
+
+ credentials_file = "/path/to/credentials/file.json"
+ composite_creds = composite_creds_call.return_value
+
+ channel = grpc_helpers_async.create_channel(
+ target, credentials_file=credentials_file, default_scopes=default_scopes
+ )
+
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=None, default_scopes=default_scopes)
+ else:
+ google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=default_scopes)
+
assert channel is grpc_secure_channel.return_value
grpc_secure_channel.assert_called_once_with(target, composite_creds)
@@ -434,7 +585,12 @@
grpc_helpers_async.create_channel(target, credentials=credentials, scopes=scopes)
grpc_secure_channel.assert_called()
- credentials.with_scopes.assert_called_once_with(scopes)
+
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None)
+ else:
+ credentials.with_scopes.assert_called_once_with(scopes)
@pytest.mark.asyncio
diff --git a/tests/unit/test_grpc_helpers.py b/tests/unit/test_grpc_helpers.py
index d6ec60a..4e0ab80 100644
--- a/tests/unit/test_grpc_helpers.py
+++ b/tests/unit/test_grpc_helpers.py
@@ -222,6 +222,7 @@
@mock.patch("grpc.composite_channel_credentials")
@mock.patch(
"google.auth.default",
+ autospec=True,
return_value=(mock.sentinel.credentials, mock.sentinel.projet),
)
@mock.patch("grpc.secure_channel")
@@ -232,7 +233,51 @@
channel = grpc_helpers.create_channel(target)
assert channel is grpc_secure_channel.return_value
- default.assert_called_once_with(scopes=None)
+
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ default.assert_called_once_with(scopes=None, default_scopes=None)
+ else:
+ default.assert_called_once_with(scopes=None)
+
+ if grpc_helpers.HAS_GRPC_GCP:
+ grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
+ else:
+ grpc_secure_channel.assert_called_once_with(target, composite_creds)
+
+
+@mock.patch("google.auth.transport.grpc.AuthMetadataPlugin", autospec=True)
+@mock.patch(
+ "google.auth.transport.requests.Request",
+ autospec=True,
+ return_value=mock.sentinel.Request
+)
+@mock.patch("grpc.composite_channel_credentials")
+@mock.patch(
+ "google.auth.default",
+ autospec=True,
+ return_value=(mock.sentinel.credentials, mock.sentinel.project),
+)
+@mock.patch("grpc.secure_channel")
+def test_create_channel_implicit_with_default_host(grpc_secure_channel, default, composite_creds_call, request, auth_metadata_plugin):
+ target = "example.com:443"
+ default_host = "example.com"
+ composite_creds = composite_creds_call.return_value
+
+ channel = grpc_helpers.create_channel(target, default_host=default_host)
+
+ assert channel is grpc_secure_channel.return_value
+
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ print(grpc_helpers._GOOGLE_AUTH_VERSION)
+ print(grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST)
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ default.assert_called_once_with(scopes=None, default_scopes=None)
+ auth_metadata_plugin.assert_called_once_with(mock.sentinel.credentials, mock.sentinel.Request, default_host=default_host)
+ else:
+ default.assert_called_once_with(scopes=None)
+ auth_metadata_plugin.assert_called_once_with(mock.sentinel.credentials, mock.sentinel.Request)
+
if grpc_helpers.HAS_GRPC_GCP:
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
else:
@@ -242,6 +287,7 @@
@mock.patch("grpc.composite_channel_credentials")
@mock.patch(
"google.auth.default",
+ autospec=True,
return_value=(mock.sentinel.credentials, mock.sentinel.projet),
)
@mock.patch("grpc.secure_channel")
@@ -254,7 +300,12 @@
grpc_helpers.create_channel(target, ssl_credentials=ssl_creds)
- default.assert_called_once_with(scopes=None)
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ default.assert_called_once_with(scopes=None, default_scopes=None)
+ else:
+ default.assert_called_once_with(scopes=None)
+
composite_creds_call.assert_called_once_with(ssl_creds, mock.ANY)
composite_creds = composite_creds_call.return_value
if grpc_helpers.HAS_GRPC_GCP:
@@ -266,6 +317,7 @@
@mock.patch("grpc.composite_channel_credentials")
@mock.patch(
"google.auth.default",
+ autospec=True,
return_value=(mock.sentinel.credentials, mock.sentinel.projet),
)
@mock.patch("grpc.secure_channel")
@@ -278,7 +330,42 @@
channel = grpc_helpers.create_channel(target, scopes=["one", "two"])
assert channel is grpc_secure_channel.return_value
- default.assert_called_once_with(scopes=["one", "two"])
+
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ default.assert_called_once_with(scopes=["one", "two"], default_scopes=None)
+ else:
+ default.assert_called_once_with(scopes=["one", "two"])
+
+ if grpc_helpers.HAS_GRPC_GCP:
+ grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
+ else:
+ grpc_secure_channel.assert_called_once_with(target, composite_creds)
+
+
+@mock.patch("grpc.composite_channel_credentials")
+@mock.patch(
+ "google.auth.default",
+ autospec=True,
+ return_value=(mock.sentinel.credentials, mock.sentinel.projet),
+)
+@mock.patch("grpc.secure_channel")
+def test_create_channel_implicit_with_default_scopes(
+ grpc_secure_channel, default, composite_creds_call
+):
+ target = "example.com:443"
+ composite_creds = composite_creds_call.return_value
+
+ channel = grpc_helpers.create_channel(target, default_scopes=["three", "four"])
+
+ assert channel is grpc_secure_channel.return_value
+
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ default.assert_called_once_with(scopes=None, default_scopes=["three", "four"])
+ else:
+ default.assert_called_once_with(scopes=["three", "four"])
+
if grpc_helpers.HAS_GRPC_GCP:
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
else:
@@ -297,7 +384,7 @@
@mock.patch("grpc.composite_channel_credentials")
-@mock.patch("google.auth.credentials.with_scopes_if_required")
+@mock.patch("google.auth.credentials.with_scopes_if_required", autospec=True)
@mock.patch("grpc.secure_channel")
def test_create_channel_explicit(grpc_secure_channel, auth_creds, composite_creds_call):
target = "example.com:443"
@@ -305,7 +392,11 @@
channel = grpc_helpers.create_channel(target, credentials=mock.sentinel.credentials)
- auth_creds.assert_called_once_with(mock.sentinel.credentials, None)
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ auth_creds.assert_called_once_with(mock.sentinel.credentials, scopes=None, default_scopes=None)
+ else:
+ auth_creds.assert_called_once_with(mock.sentinel.credentials, scopes=None)
assert channel is grpc_secure_channel.return_value
if grpc_helpers.HAS_GRPC_GCP:
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
@@ -327,7 +418,39 @@
target, credentials=credentials, scopes=scopes
)
- credentials.with_scopes.assert_called_once_with(scopes)
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None)
+ else:
+ credentials.with_scopes.assert_called_once_with(scopes)
+
+ assert channel is grpc_secure_channel.return_value
+ if grpc_helpers.HAS_GRPC_GCP:
+ grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
+ else:
+ grpc_secure_channel.assert_called_once_with(target, composite_creds)
+
+
+@mock.patch("grpc.composite_channel_credentials")
+@mock.patch("grpc.secure_channel")
+def test_create_channel_explicit_default_scopes(grpc_secure_channel, composite_creds_call):
+ target = "example.com:443"
+ default_scopes = ["3", "4"]
+ composite_creds = composite_creds_call.return_value
+
+ credentials = mock.create_autospec(google.auth.credentials.Scoped, instance=True)
+ credentials.requires_scopes = True
+
+ channel = grpc_helpers.create_channel(
+ target, credentials=credentials, default_scopes=default_scopes
+ )
+
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ credentials.with_scopes.assert_called_once_with(scopes=None, default_scopes=default_scopes)
+ else:
+ credentials.with_scopes.assert_called_once_with(scopes=default_scopes)
+
assert channel is grpc_secure_channel.return_value
if grpc_helpers.HAS_GRPC_GCP:
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
@@ -362,6 +485,7 @@
@mock.patch("grpc.secure_channel")
@mock.patch(
"google.auth.load_credentials_from_file",
+ autospec=True,
return_value=(mock.sentinel.credentials, mock.sentinel.project)
)
def test_create_channel_with_credentials_file(load_credentials_from_file, grpc_secure_channel, composite_creds_call):
@@ -374,7 +498,11 @@
target, credentials_file=credentials_file
)
- google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=None)
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=None, default_scopes=None)
+ else:
+ google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=None)
assert channel is grpc_secure_channel.return_value
if grpc_helpers.HAS_GRPC_GCP:
@@ -387,6 +515,7 @@
@mock.patch("grpc.secure_channel")
@mock.patch(
"google.auth.load_credentials_from_file",
+ autospec=True,
return_value=(mock.sentinel.credentials, mock.sentinel.project)
)
def test_create_channel_with_credentials_file_and_scopes(load_credentials_from_file, grpc_secure_channel, composite_creds_call):
@@ -400,7 +529,43 @@
target, credentials_file=credentials_file, scopes=scopes
)
- google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes)
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes, default_scopes=None)
+ else:
+ google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes)
+
+ assert channel is grpc_secure_channel.return_value
+ if grpc_helpers.HAS_GRPC_GCP:
+ grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
+ else:
+ grpc_secure_channel.assert_called_once_with(target, composite_creds)
+
+
+@mock.patch("grpc.composite_channel_credentials")
+@mock.patch("grpc.secure_channel")
+@mock.patch(
+ "google.auth.load_credentials_from_file",
+ autospec=True,
+ return_value=(mock.sentinel.credentials, mock.sentinel.project)
+)
+def test_create_channel_with_credentials_file_and_default_scopes(load_credentials_from_file, grpc_secure_channel, composite_creds_call):
+ target = "example.com:443"
+ default_scopes = ["3", "4"]
+
+ credentials_file = "/path/to/credentials/file.json"
+ composite_creds = composite_creds_call.return_value
+
+ channel = grpc_helpers.create_channel(
+ target, credentials_file=credentials_file, default_scopes=default_scopes
+ )
+
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ load_credentials_from_file.assert_called_once_with(credentials_file, scopes=None, default_scopes=default_scopes)
+ else:
+ load_credentials_from_file.assert_called_once_with(credentials_file, scopes=default_scopes)
+
assert channel is grpc_secure_channel.return_value
if grpc_helpers.HAS_GRPC_GCP:
grpc_secure_channel.assert_called_once_with(target, composite_creds, None)
@@ -421,7 +586,12 @@
grpc_helpers.create_channel(target, credentials=credentials, scopes=scopes)
grpc_gcp_secure_channel.assert_called()
- credentials.with_scopes.assert_called_once_with(scopes)
+
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None)
+ else:
+ credentials.with_scopes.assert_called_once_with(scopes)
@pytest.mark.skipif(grpc_helpers.HAS_GRPC_GCP, reason="grpc_gcp module not available")
@@ -435,7 +605,12 @@
grpc_helpers.create_channel(target, credentials=credentials, scopes=scopes)
grpc_secure_channel.assert_called()
- credentials.with_scopes.assert_called_once_with(scopes)
+
+ # TODO: remove this if/else once google-auth >= 1.25.0 is required
+ if grpc_helpers._GOOGLE_AUTH_HAS_DEFAULT_SCOPES_AND_DEFAULT_HOST:
+ credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None)
+ else:
+ credentials.with_scopes.assert_called_once_with(scopes)
class TestChannelStub(object):