From 4be38d3a87df5d39ea1a80bd67d6960cbb5658ff Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 14 Jan 2021 01:37:51 +0000 Subject: [PATCH 01/12] feat: distinguish default vs. user-defind scopes --- google/api_core/grpc_helpers.py | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index 0ccbe126..4ee81273 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -179,9 +179,11 @@ def wrap_errors(callable_): 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, + audience=None): """Create the composite credentials for secure channels. Args: @@ -191,12 +193,16 @@ def _create_composite_credentials( 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. + audience (str): The audience needed for authentication. Returns: grpc.ChannelCredentials: The composed channel credentials object. @@ -209,12 +215,15 @@ def _create_composite_credentials( "'credentials' and 'credentials_file' are mutually exclusive." ) + if credentials_file: - credentials, _ = google.auth.load_credentials_from_file(credentials_file, scopes=scopes) + credentials, _ = google.auth.load_credentials_from_file(credentials_file, + scopes=scopes, default_scopes=default_scopes) elif credentials: - credentials = google.auth.credentials.with_scopes_if_required(credentials, scopes) + credentials = google.auth.credentials.with_scopes_if_required(credentials, + scopes=scopes, default_scopes=default_scopes) else: - credentials, _ = google.auth.default(scopes=scopes) + credentials, _ = google.auth.default(scopes=scopes, default_scopes=default_scopes) if quota_project_id and isinstance(credentials, google.auth.credentials.CredentialsWithQuotaProject): credentials = credentials.with_quota_project(quota_project_id) @@ -223,7 +232,7 @@ def _create_composite_credentials( # Create the metadata plugin for inserting the authorization header. metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin( - credentials, request + credentials, request, audience=audience, ) # Create a set of grpc.CallCredentials using the metadata plugin. @@ -245,6 +254,8 @@ def create_channel( ssl_credentials=None, credentials_file=None, quota_project_id=None, + default_scopes=None, + audience=None, **kwargs): """Create a secure channel with credentials. @@ -253,6 +264,9 @@ def create_channel( credentials (google.auth.credentials.Credentials): The credentials. If not specified, then this function will attempt to ascertain the credentials from the environment using :func:`google.auth.default`. + 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`. @@ -262,6 +276,11 @@ def create_channel( :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. + audience (str): The canonical audience that identfies the service + address. This is often the same as the targe. It will differ + for example of the target is pubsub.mtls.googleapis.com and the + audience is pubsub.googleapis.com + kwargs: Additional key-word args passed to :func:`grpc_gcp.secure_channel` or :func:`grpc.secure_channel`. @@ -275,9 +294,11 @@ def create_channel( 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, + audience=audience, ) if HAS_GRPC_GCP: From f6b38ca54414147412060f1b18f3a3ddc0849f00 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Tue, 19 Jan 2021 22:08:38 +0000 Subject: [PATCH 02/12] fix: rename to 'default_host' --- google/api_core/grpc_helpers.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index 4ee81273..9e793b7c 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -183,7 +183,8 @@ def _create_composite_credentials( scopes=None, ssl_credentials=None, quota_project_id=None, - audience=None): + default_host=None + ): """Create the composite credentials for secure channels. Args: @@ -202,7 +203,8 @@ def _create_composite_credentials( 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. - audience (str): The audience needed for authentication. + default_host (str): The default endpoint. e.g., "pubsub.googleapis.com" + Returns: grpc.ChannelCredentials: The composed channel credentials object. @@ -232,7 +234,7 @@ def _create_composite_credentials( # Create the metadata plugin for inserting the authorization header. metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin( - credentials, request, audience=audience, + credentials, request, default_host=default_host, ) # Create a set of grpc.CallCredentials using the metadata plugin. @@ -255,7 +257,7 @@ def create_channel( credentials_file=None, quota_project_id=None, default_scopes=None, - audience=None, + default_host=None, **kwargs): """Create a secure channel with credentials. @@ -276,11 +278,7 @@ def create_channel( :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. - audience (str): The canonical audience that identfies the service - address. This is often the same as the targe. It will differ - for example of the target is pubsub.mtls.googleapis.com and the - audience is pubsub.googleapis.com - + 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`. @@ -298,7 +296,7 @@ def create_channel( scopes=scopes, ssl_credentials=ssl_credentials, quota_project_id=quota_project_id, - audience=audience, + default_host=default_host, ) if HAS_GRPC_GCP: From 77f7fb462d536f46dd2dbc6c327851f9af13a655 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Tue, 19 Jan 2021 23:15:28 +0000 Subject: [PATCH 03/12] test: adjust existing unit tests --- google/api_core/grpc_helpers.py | 5 ++--- noxfile.py | 3 +++ tests/asyncio/test_grpc_helpers_async.py | 16 ++++++++-------- tests/unit/test_grpc_helpers.py | 18 +++++++++--------- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index 9e793b7c..3954e42f 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -203,8 +203,7 @@ def _create_composite_credentials( 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" - + default_host (str): The default endpoint. e.g., "pubsub.googleapis.com". Returns: grpc.ChannelCredentials: The composed channel credentials object. @@ -278,7 +277,7 @@ def create_channel( :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_host (str): The default endpoint. e.g., "pubsub.googleapis.com" + 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`. diff --git a/noxfile.py b/noxfile.py index 650cef28..fbef307f 100644 --- a/noxfile.py +++ b/noxfile.py @@ -50,6 +50,9 @@ def default(session): session.install("mock", "pytest", "pytest-cov", "grpcio >= 1.0.2") session.install("-e", ".", "-c", constraints_path) + # REMOVE ME: Temporarily install google-auth from a branch + session.install("-e", "git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth") + pytest_args = [ "python", "-m", diff --git a/tests/asyncio/test_grpc_helpers_async.py b/tests/asyncio/test_grpc_helpers_async.py index 766e11a9..0bee7011 100644 --- a/tests/asyncio/test_grpc_helpers_async.py +++ b/tests/asyncio/test_grpc_helpers_async.py @@ -273,7 +273,7 @@ def test_create_channel_implicit(grpc_secure_channel, default, composite_creds_c channel = grpc_helpers_async.create_channel(target) assert channel is grpc_secure_channel.return_value - default.assert_called_once_with(scopes=None) + default.assert_called_once_with(scopes=None, default_scopes=None) grpc_secure_channel.assert_called_once_with(target, composite_creds) @@ -292,7 +292,7 @@ def test_create_channel_implicit_with_ssl_creds( grpc_helpers_async.create_channel(target, ssl_credentials=ssl_creds) - default.assert_called_once_with(scopes=None) + default.assert_called_once_with(scopes=None, default_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) @@ -313,7 +313,7 @@ def test_create_channel_implicit_with_scopes( 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"]) + default.assert_called_once_with(scopes=["one", "two"], default_scopes=None) grpc_secure_channel.assert_called_once_with(target, composite_creds) @@ -339,7 +339,7 @@ def test_create_channel_explicit(grpc_secure_channel, auth_creds, composite_cred channel = grpc_helpers_async.create_channel(target, credentials=mock.sentinel.credentials) - auth_creds.assert_called_once_with(mock.sentinel.credentials, None) + auth_creds.assert_called_once_with(mock.sentinel.credentials, scopes=None, default_scopes=None) assert channel is grpc_secure_channel.return_value grpc_secure_channel.assert_called_once_with(target, composite_creds) @@ -358,7 +358,7 @@ def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_cal target, credentials=credentials, scopes=scopes ) - credentials.with_scopes.assert_called_once_with(scopes) + credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None) assert channel is grpc_secure_channel.return_value grpc_secure_channel.assert_called_once_with(target, composite_creds) @@ -396,7 +396,7 @@ def test_create_channnel_with_credentials_file(load_credentials_from_file, grpc_ target, credentials_file=credentials_file ) - google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=None) + google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=None, default_scopes=None) assert channel is grpc_secure_channel.return_value grpc_secure_channel.assert_called_once_with(target, composite_creds) @@ -418,7 +418,7 @@ def test_create_channel_with_credentials_file_and_scopes(load_credentials_from_f target, credentials_file=credentials_file, scopes=scopes ) - google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes) + google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes, default_scopes=None) assert channel is grpc_secure_channel.return_value grpc_secure_channel.assert_called_once_with(target, composite_creds) @@ -434,7 +434,7 @@ def test_create_channel_without_grpc_gcp(grpc_secure_channel): grpc_helpers_async.create_channel(target, credentials=credentials, scopes=scopes) grpc_secure_channel.assert_called() - credentials.with_scopes.assert_called_once_with(scopes) + credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None) @pytest.mark.asyncio diff --git a/tests/unit/test_grpc_helpers.py b/tests/unit/test_grpc_helpers.py index d6ec60a5..77da2de3 100644 --- a/tests/unit/test_grpc_helpers.py +++ b/tests/unit/test_grpc_helpers.py @@ -232,7 +232,7 @@ def test_create_channel_implicit(grpc_secure_channel, default, composite_creds_c channel = grpc_helpers.create_channel(target) assert channel is grpc_secure_channel.return_value - default.assert_called_once_with(scopes=None) + default.assert_called_once_with(scopes=None, default_scopes=None) if grpc_helpers.HAS_GRPC_GCP: grpc_secure_channel.assert_called_once_with(target, composite_creds, None) else: @@ -254,7 +254,7 @@ def test_create_channel_implicit_with_ssl_creds( grpc_helpers.create_channel(target, ssl_credentials=ssl_creds) - default.assert_called_once_with(scopes=None) + default.assert_called_once_with(scopes=None, default_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: @@ -278,7 +278,7 @@ def test_create_channel_implicit_with_scopes( 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"]) + default.assert_called_once_with(scopes=["one", "two"], default_scopes=None) if grpc_helpers.HAS_GRPC_GCP: grpc_secure_channel.assert_called_once_with(target, composite_creds, None) else: @@ -305,7 +305,7 @@ def test_create_channel_explicit(grpc_secure_channel, auth_creds, composite_cred channel = grpc_helpers.create_channel(target, credentials=mock.sentinel.credentials) - auth_creds.assert_called_once_with(mock.sentinel.credentials, None) + auth_creds.assert_called_once_with(mock.sentinel.credentials, scopes=None, default_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 +327,7 @@ def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_cal target, credentials=credentials, scopes=scopes ) - credentials.with_scopes.assert_called_once_with(scopes) + credentials.with_scopes.assert_called_once_with(scopes, default_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) @@ -374,7 +374,7 @@ def test_create_channel_with_credentials_file(load_credentials_from_file, grpc_s target, credentials_file=credentials_file ) - google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=None) + google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=None, default_scopes=None) assert channel is grpc_secure_channel.return_value if grpc_helpers.HAS_GRPC_GCP: @@ -400,7 +400,7 @@ def test_create_channel_with_credentials_file_and_scopes(load_credentials_from_f target, credentials_file=credentials_file, scopes=scopes ) - google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes) + google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes, default_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) @@ -421,7 +421,7 @@ def test_create_channel_with_grpc_gcp(grpc_gcp_secure_channel): grpc_helpers.create_channel(target, credentials=credentials, scopes=scopes) grpc_gcp_secure_channel.assert_called() - credentials.with_scopes.assert_called_once_with(scopes) + credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None) @pytest.mark.skipif(grpc_helpers.HAS_GRPC_GCP, reason="grpc_gcp module not available") @@ -435,7 +435,7 @@ def test_create_channel_without_grpc_gcp(grpc_secure_channel): grpc_helpers.create_channel(target, credentials=credentials, scopes=scopes) grpc_secure_channel.assert_called() - credentials.with_scopes.assert_called_once_with(scopes) + credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None) class TestChannelStub(object): From 8fb64a76777362d5e4511ea4bb49a0bff844097f Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Tue, 19 Jan 2021 23:23:11 +0000 Subject: [PATCH 04/12] feat: add default_scopes to async --- google/api_core/grpc_helpers.py | 5 ++--- google/api_core/grpc_helpers_async.py | 7 +++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index 3954e42f..012e8518 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -265,9 +265,6 @@ def create_channel( credentials (google.auth.credentials.Credentials): The credentials. If not specified, then this function will attempt to ascertain the credentials from the environment using :func:`google.auth.default`. - 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`. @@ -277,6 +274,8 @@ def create_channel( :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`. diff --git a/google/api_core/grpc_helpers_async.py b/google/api_core/grpc_helpers_async.py index 9a994e9f..14eb5a13 100644 --- a/google/api_core/grpc_helpers_async.py +++ b/google/api_core/grpc_helpers_async.py @@ -213,6 +213,8 @@ def create_channel( 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 @@ def create_channel( :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 @@ def create_channel( 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) From 9df4d1b10362bf8825e75175ca7832318b360b42 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Tue, 19 Jan 2021 23:43:57 +0000 Subject: [PATCH 05/12] test: add unit tests for default_scopes --- tests/asyncio/test_grpc_helpers_async.py | 63 +++++++++++++++++++++ tests/unit/test_grpc_helpers.py | 72 ++++++++++++++++++++++++ 2 files changed, 135 insertions(+) diff --git a/tests/asyncio/test_grpc_helpers_async.py b/tests/asyncio/test_grpc_helpers_async.py index 0bee7011..29c885ca 100644 --- a/tests/asyncio/test_grpc_helpers_async.py +++ b/tests/asyncio/test_grpc_helpers_async.py @@ -317,6 +317,25 @@ def test_create_channel_implicit_with_scopes( grpc_secure_channel.assert_called_once_with(target, composite_creds) +@mock.patch("grpc.composite_channel_credentials") +@mock.patch( + "google.auth.default", + 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, scopes=["one", "two"], default_scopes=["three", "four"]) + + assert channel is grpc_secure_channel.return_value + default.assert_called_once_with(scopes=["one", "two"], default_scopes=["three", "four"]) + grpc_secure_channel.assert_called_once_with(target, composite_creds) + + def test_create_channel_explicit_with_duplicate_credentials(): target = "example:443" @@ -363,6 +382,27 @@ def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_cal 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" + scopes = ["1", "2"] + 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, scopes=scopes, default_scopes=default_scopes + ) + + credentials.with_scopes.assert_called_once_with(scopes, default_scopes=default_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_with_quota_project(grpc_secure_channel, composite_creds_call): @@ -423,6 +463,29 @@ def test_create_channel_with_credentials_file_and_scopes(load_credentials_from_f 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", + 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" + scopes = ["1", "2"] + 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, scopes=scopes, default_scopes=default_scopes + ) + + google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes, default_scopes=default_scopes) + assert channel is grpc_secure_channel.return_value + grpc_secure_channel.assert_called_once_with(target, composite_creds) + + @pytest.mark.skipif(grpc_helpers_async.HAS_GRPC_GCP, reason="grpc_gcp module not available") @mock.patch("grpc.experimental.aio.secure_channel") def test_create_channel_without_grpc_gcp(grpc_secure_channel): diff --git a/tests/unit/test_grpc_helpers.py b/tests/unit/test_grpc_helpers.py index 77da2de3..d7e07c47 100644 --- a/tests/unit/test_grpc_helpers.py +++ b/tests/unit/test_grpc_helpers.py @@ -285,6 +285,28 @@ def test_create_channel_implicit_with_scopes( grpc_secure_channel.assert_called_once_with(target, composite_creds) +@mock.patch("grpc.composite_channel_credentials") +@mock.patch( + "google.auth.default", + 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, scopes=["one", "two"], default_scopes=["three", "four"]) + + assert channel is grpc_secure_channel.return_value + default.assert_called_once_with(scopes=["one", "two"], default_scopes=["three", "four"]) + 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) + + def test_create_channel_explicit_with_duplicate_credentials(): target = "example.com:443" @@ -333,6 +355,29 @@ def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_cal 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" + scopes = ["1", "2"] + 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, scopes=scopes, default_scopes=default_scopes + ) + + credentials.with_scopes.assert_called_once_with(scopes, default_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) + else: + grpc_secure_channel.assert_called_once_with(target, composite_creds) @mock.patch("grpc.composite_channel_credentials") @@ -408,6 +453,33 @@ def test_create_channel_with_credentials_file_and_scopes(load_credentials_from_f 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", + 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" + scopes = ["1", "2"] + 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, scopes=scopes, default_scopes=default_scopes + ) + + load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes, default_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) + else: + grpc_secure_channel.assert_called_once_with(target, composite_creds) + + + @pytest.mark.skipif( not grpc_helpers.HAS_GRPC_GCP, reason="grpc_gcp module not available" ) From ba36db69bdab3c3c89895797b64a869b7e51cabe Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Wed, 20 Jan 2021 00:33:13 +0000 Subject: [PATCH 06/12] test: test default_host --- google/api_core/grpc_helpers.py | 18 ++++++++------ tests/asyncio/test_grpc_helpers_async.py | 25 ++++++++++++++++++- tests/unit/test_grpc_helpers.py | 31 ++++++++++++++++++++++-- 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index 012e8518..9ca2c5d5 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -183,8 +183,7 @@ def _create_composite_credentials( scopes=None, ssl_credentials=None, quota_project_id=None, - default_host=None - ): + default_host=None): """Create the composite credentials for secure channels. Args: @@ -216,13 +215,18 @@ def _create_composite_credentials( "'credentials' and 'credentials_file' are mutually exclusive." ) - if credentials_file: - credentials, _ = google.auth.load_credentials_from_file(credentials_file, - scopes=scopes, default_scopes=default_scopes) + credentials, _ = google.auth.load_credentials_from_file( + credentials_file, + scopes=scopes, + default_scopes=default_scopes + ) elif credentials: - credentials = google.auth.credentials.with_scopes_if_required(credentials, - scopes=scopes, default_scopes=default_scopes) + credentials = google.auth.credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=default_scopes + ) else: credentials, _ = google.auth.default(scopes=scopes, default_scopes=default_scopes) diff --git a/tests/asyncio/test_grpc_helpers_async.py b/tests/asyncio/test_grpc_helpers_async.py index 29c885ca..c60b8d59 100644 --- a/tests/asyncio/test_grpc_helpers_async.py +++ b/tests/asyncio/test_grpc_helpers_async.py @@ -277,6 +277,30 @@ def test_create_channel_implicit(grpc_secure_channel, default, composite_creds_c grpc_secure_channel.assert_called_once_with(target, composite_creds) +@mock.patch("google.auth.transport.grpc.AuthMetadataPlugin") +@mock.patch( + "google.auth.transport.requests.Request", + return_value=mock.sentinel.Request +) +@mock.patch("grpc.composite_channel_credentials") +@mock.patch( + "google.auth.default", + 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 + 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) + grpc_secure_channel.assert_called_once_with(target, composite_creds) + + @mock.patch("grpc.composite_channel_credentials") @mock.patch( "google.auth.default", @@ -402,7 +426,6 @@ def test_create_channel_explicit_default_scopes(grpc_secure_channel, composite_c 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_with_quota_project(grpc_secure_channel, composite_creds_call): diff --git a/tests/unit/test_grpc_helpers.py b/tests/unit/test_grpc_helpers.py index d7e07c47..28612c60 100644 --- a/tests/unit/test_grpc_helpers.py +++ b/tests/unit/test_grpc_helpers.py @@ -239,6 +239,34 @@ def test_create_channel_implicit(grpc_secure_channel, default, composite_creds_c grpc_secure_channel.assert_called_once_with(target, composite_creds) +@mock.patch("google.auth.transport.grpc.AuthMetadataPlugin") +@mock.patch( + "google.auth.transport.requests.Request", + return_value=mock.sentinel.Request +) +@mock.patch("grpc.composite_channel_credentials") +@mock.patch( + "google.auth.default", + 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 + 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) + + 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", @@ -355,7 +383,7 @@ def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_cal 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") @@ -479,7 +507,6 @@ def test_create_channel_with_credentials_file_and_default_scopes(load_credential grpc_secure_channel.assert_called_once_with(target, composite_creds) - @pytest.mark.skipif( not grpc_helpers.HAS_GRPC_GCP, reason="grpc_gcp module not available" ) From 46d64836fe080f55d407f9ec35c87b6848fa5ecf Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Wed, 3 Feb 2021 18:00:18 +0000 Subject: [PATCH 07/12] refactor: tweak code to not fail on older google-auth versions --- google/api_core/grpc_helpers.py | 58 +++++++++++++++++++++++++-------- noxfile.py | 2 +- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index 9ca2c5d5..a1eae3d7 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -216,19 +216,41 @@ def _create_composite_credentials( ) if credentials_file: - credentials, _ = google.auth.load_credentials_from_file( - credentials_file, - scopes=scopes, - default_scopes=default_scopes - ) + try: + credentials, _ = google.auth.load_credentials_from_file( + credentials_file, + scopes=scopes, + default_scopes=default_scopes + ) + # google-auth < x.x.x does not have `default_scopes` + # TODO: remove this try/except once google-auth >= x.x.x is required + except TypeError: + 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=scopes, - default_scopes=default_scopes - ) + try: + credentials = google.auth.credentials.with_scopes_if_required( + credentials, + scopes=scopes, + default_scopes=default_scopes + ) + # google-auth < x.x.x does not have `default_scopes` + # TODO: remove this try/except once google-auth >= x.x.x is required + except TypeError: + credentials = google.auth.credentials.with_scopes_if_required( + credentials, + scopes=scopes or default_scopes, + ) + else: - credentials, _ = google.auth.default(scopes=scopes, default_scopes=default_scopes) + try: + credentials, _ = google.auth.default(scopes=scopes, default_scopes=default_scopes) + # google-auth < x.x.x does not have `default_scopes` + # TODO: remove this try/except once google-auth >= x.x.x is required + except TypeError: + 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) @@ -236,9 +258,17 @@ def _create_composite_credentials( request = google.auth.transport.requests.Request() # Create the metadata plugin for inserting the authorization header. - metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin( - credentials, request, default_host=default_host, - ) + + # google-auth < x.x.x does not have `default_host` + # TODO: remove this try/except once google-auth >= x.x.x is required + try: + metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin( + credentials, request, default_host=default_host, + ) + except: + 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) diff --git a/noxfile.py b/noxfile.py index fbef307f..147313b5 100644 --- a/noxfile.py +++ b/noxfile.py @@ -51,7 +51,7 @@ def default(session): session.install("-e", ".", "-c", constraints_path) # REMOVE ME: Temporarily install google-auth from a branch - session.install("-e", "git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth") + # session.install("-e", "git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth") pytest_args = [ "python", From 3e28831bc63758f72e5dc9fa7a9b61bf4a29f633 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Wed, 3 Feb 2021 18:02:12 +0000 Subject: [PATCH 08/12] test: uncomment install auth from branch --- noxfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index 147313b5..fbef307f 100644 --- a/noxfile.py +++ b/noxfile.py @@ -51,7 +51,7 @@ def default(session): session.install("-e", ".", "-c", constraints_path) # REMOVE ME: Temporarily install google-auth from a branch - # session.install("-e", "git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth") + session.install("-e", "git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth") pytest_args = [ "python", From 852e702293f33e281725470cbc06962eefa7d13d Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 4 Feb 2021 00:29:53 +0000 Subject: [PATCH 09/12] test: install latest google-auth --- noxfile.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/noxfile.py b/noxfile.py index fbef307f..650cef28 100644 --- a/noxfile.py +++ b/noxfile.py @@ -50,9 +50,6 @@ def default(session): session.install("mock", "pytest", "pytest-cov", "grpcio >= 1.0.2") session.install("-e", ".", "-c", constraints_path) - # REMOVE ME: Temporarily install google-auth from a branch - session.install("-e", "git+https://github.com/googleapis/google-auth-library-python.git@self-signed-jwt#egg=google-auth") - pytest_args = [ "python", "-m", From 724cdaa09289c4ac71937d10ea1b31e5fc68510d Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 4 Feb 2021 00:46:05 +0000 Subject: [PATCH 10/12] test: update tests --- google/api_core/grpc_helpers.py | 9 +++------ tests/asyncio/test_grpc_helpers_async.py | 22 +++++++++++++++++++--- tests/unit/test_grpc_helpers.py | 22 +++++++++++++++++++--- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index a1eae3d7..1fc2a2ee 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -236,8 +236,7 @@ def _create_composite_credentials( scopes=scopes, default_scopes=default_scopes ) - # google-auth < x.x.x does not have `default_scopes` - # TODO: remove this try/except once google-auth >= x.x.x is required + # TODO: remove this try/except once google-auth >= 1.25.0 is required except TypeError: credentials = google.auth.credentials.with_scopes_if_required( credentials, @@ -247,8 +246,7 @@ def _create_composite_credentials( else: try: credentials, _ = google.auth.default(scopes=scopes, default_scopes=default_scopes) - # google-auth < x.x.x does not have `default_scopes` - # TODO: remove this try/except once google-auth >= x.x.x is required + # TODO: remove this try/except once google-auth >= 1.25.0 is required except TypeError: credentials, _ = google.auth.default(scopes=scopes or default_scopes) @@ -259,8 +257,7 @@ def _create_composite_credentials( # Create the metadata plugin for inserting the authorization header. - # google-auth < x.x.x does not have `default_host` - # TODO: remove this try/except once google-auth >= x.x.x is required + # TODO: remove this try/except once google-auth >= 1.25.0 is required try: metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin( credentials, request, default_host=default_host, diff --git a/tests/asyncio/test_grpc_helpers_async.py b/tests/asyncio/test_grpc_helpers_async.py index c60b8d59..f0029831 100644 --- a/tests/asyncio/test_grpc_helpers_async.py +++ b/tests/asyncio/test_grpc_helpers_async.py @@ -15,6 +15,7 @@ import grpc from grpc.experimental import aio import mock +import pkg_resources import pytest from google.api_core import exceptions @@ -401,7 +402,12 @@ def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_cal target, credentials=credentials, scopes=scopes ) - credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None) + # TODO: remove if/else once google-auth >= 1.25.0 is required + if pkg_resources.get_distribution("google-auth").version >= "1.25.0": + 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 grpc_secure_channel.assert_called_once_with(target, composite_creds) @@ -421,7 +427,12 @@ def test_create_channel_explicit_default_scopes(grpc_secure_channel, composite_c target, credentials=credentials, scopes=scopes, default_scopes=default_scopes ) - credentials.with_scopes.assert_called_once_with(scopes, default_scopes=default_scopes) + # TODO: remove if/else once google-auth >= 1.25.0 is required + if pkg_resources.get_distribution("google-auth").version >= "1.25.0": + credentials.with_scopes.assert_called_once_with(scopes, default_scopes=default_scopes) + else: + 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) @@ -520,7 +531,12 @@ def test_create_channel_without_grpc_gcp(grpc_secure_channel): grpc_helpers_async.create_channel(target, credentials=credentials, scopes=scopes) grpc_secure_channel.assert_called() - credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None) + + # TODO: remove if/else once google-auth >= 1.25.0 is required + if pkg_resources.get_distribution("google-auth").version >= "1.25.0": + 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 28612c60..7af6e3ac 100644 --- a/tests/unit/test_grpc_helpers.py +++ b/tests/unit/test_grpc_helpers.py @@ -14,6 +14,7 @@ import grpc import mock +import pkg_resources import pytest from google.api_core import exceptions @@ -377,7 +378,12 @@ def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_cal target, credentials=credentials, scopes=scopes ) - credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None) + # TODO: remove if/else once google-auth >= 1.25.0 is required + if pkg_resources.get_distribution("google-auth").version >= "1.25.0": + 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) @@ -400,7 +406,12 @@ def test_create_channel_explicit_default_scopes(grpc_secure_channel, composite_c target, credentials=credentials, scopes=scopes, default_scopes=default_scopes ) - credentials.with_scopes.assert_called_once_with(scopes, default_scopes=default_scopes) + # TODO: remove if/else once google-auth >= 1.25.0 is required + if pkg_resources.get_distribution("google-auth").version >= "1.25.0": + credentials.with_scopes.assert_called_once_with(scopes, default_scopes=default_scopes) + 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) @@ -534,7 +545,12 @@ def test_create_channel_without_grpc_gcp(grpc_secure_channel): grpc_helpers.create_channel(target, credentials=credentials, scopes=scopes) grpc_secure_channel.assert_called() - credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None) + + # TODO: remove if/else once google-auth >= 1.25.0 is required + if pkg_resources.get_distribution("google-auth").version >= "1.25.0": + credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None) + else: + credentials.with_scopes.assert_called_once_with(scopes) class TestChannelStub(object): From 15e50ba76d67e84fc288d2c8ac919495186233c4 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Thu, 4 Feb 2021 21:36:03 +0000 Subject: [PATCH 11/12] test: use autospec for auth functions, fix coverage --- google/api_core/grpc_helpers.py | 11 ++--- tests/unit/test_grpc_helpers.py | 86 +++++++++++++++++++++++++++------ 2 files changed, 77 insertions(+), 20 deletions(-) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index 1fc2a2ee..1522d866 100644 --- a/google/api_core/grpc_helpers.py +++ b/google/api_core/grpc_helpers.py @@ -216,27 +216,26 @@ def _create_composite_credentials( ) if credentials_file: + # TODO: remove this try/except once google-auth >= 1.25.0 is required try: credentials, _ = google.auth.load_credentials_from_file( credentials_file, scopes=scopes, default_scopes=default_scopes ) - # google-auth < x.x.x does not have `default_scopes` - # TODO: remove this try/except once google-auth >= x.x.x is required except TypeError: credentials, _ = google.auth.load_credentials_from_file( credentials_file, scopes=scopes or default_scopes, ) elif credentials: + # TODO: remove this try/except once google-auth >= 1.25.0 is required try: credentials = google.auth.credentials.with_scopes_if_required( credentials, scopes=scopes, default_scopes=default_scopes ) - # TODO: remove this try/except once google-auth >= 1.25.0 is required except TypeError: credentials = google.auth.credentials.with_scopes_if_required( credentials, @@ -244,10 +243,10 @@ def _create_composite_credentials( ) else: + # TODO: remove this try/except once google-auth >= 1.25.0 is required try: credentials, _ = google.auth.default(scopes=scopes, default_scopes=default_scopes) - # TODO: remove this try/except once google-auth >= 1.25.0 is required - except TypeError: + except TypeError: credentials, _ = google.auth.default(scopes=scopes or default_scopes) if quota_project_id and isinstance(credentials, google.auth.credentials.CredentialsWithQuotaProject): @@ -262,7 +261,7 @@ def _create_composite_credentials( metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin( credentials, request, default_host=default_host, ) - except: + except TypeError: metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin( credentials, request ) diff --git a/tests/unit/test_grpc_helpers.py b/tests/unit/test_grpc_helpers.py index 7af6e3ac..880d2771 100644 --- a/tests/unit/test_grpc_helpers.py +++ b/tests/unit/test_grpc_helpers.py @@ -14,6 +14,7 @@ import grpc import mock +from packaging import version import pkg_resources import pytest @@ -223,6 +224,7 @@ def test_wrap_errors_streaming(wrap_stream_errors): @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") @@ -233,21 +235,29 @@ def test_create_channel_implicit(grpc_secure_channel, default, composite_creds_c channel = grpc_helpers.create_channel(target) assert channel is grpc_secure_channel.return_value - default.assert_called_once_with(scopes=None, default_scopes=None) + + # TODO: remove if/else once google-auth >= 1.25.0 is required + if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + 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") +@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") @@ -259,9 +269,15 @@ def test_create_channel_implicit_with_default_host(grpc_secure_channel, default, channel = grpc_helpers.create_channel(target, default_host=default_host) assert channel is grpc_secure_channel.return_value - 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) + # TODO: remove if/else once google-auth >= 1.25.0 is required + if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + 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: @@ -271,6 +287,7 @@ def test_create_channel_implicit_with_default_host(grpc_secure_channel, default, @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") @@ -283,7 +300,12 @@ def test_create_channel_implicit_with_ssl_creds( grpc_helpers.create_channel(target, ssl_credentials=ssl_creds) - default.assert_called_once_with(scopes=None, default_scopes=None) + # TODO: remove if/else once google-auth >= 1.25.0 is required + if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + 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: @@ -295,6 +317,7 @@ def test_create_channel_implicit_with_ssl_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") @@ -307,7 +330,13 @@ def test_create_channel_implicit_with_scopes( 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"], default_scopes=None) + + # TODO: remove if/else once google-auth >= 1.25.0 is required + if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + 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: @@ -317,6 +346,7 @@ def test_create_channel_implicit_with_scopes( @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") @@ -329,7 +359,13 @@ def test_create_channel_implicit_with_default_scopes( channel = grpc_helpers.create_channel(target, scopes=["one", "two"], default_scopes=["three", "four"]) assert channel is grpc_secure_channel.return_value - default.assert_called_once_with(scopes=["one", "two"], default_scopes=["three", "four"]) + + # TODO: remove if/else once google-auth >= 1.25.0 is required + if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + default.assert_called_once_with(scopes=["one", "two"], default_scopes=["three", "four"]) + 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: @@ -379,7 +415,7 @@ def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_cal ) # TODO: remove if/else once google-auth >= 1.25.0 is required - if pkg_resources.get_distribution("google-auth").version >= "1.25.0": + if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None) else: credentials.with_scopes.assert_called_once_with(scopes) @@ -407,7 +443,7 @@ def test_create_channel_explicit_default_scopes(grpc_secure_channel, composite_c ) # TODO: remove if/else once google-auth >= 1.25.0 is required - if pkg_resources.get_distribution("google-auth").version >= "1.25.0": + if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): credentials.with_scopes.assert_called_once_with(scopes, default_scopes=default_scopes) else: credentials.with_scopes.assert_called_once_with(scopes) @@ -446,6 +482,7 @@ def test_create_channel_explicit_with_quota_project(grpc_secure_channel, composi @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): @@ -458,7 +495,11 @@ def test_create_channel_with_credentials_file(load_credentials_from_file, grpc_s target, credentials_file=credentials_file ) - google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=None, default_scopes=None) + # TODO: remove if/else once google-auth >= 1.25.0 is required + if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + 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: @@ -471,6 +512,7 @@ def test_create_channel_with_credentials_file(load_credentials_from_file, grpc_s @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): @@ -484,7 +526,12 @@ def test_create_channel_with_credentials_file_and_scopes(load_credentials_from_f target, credentials_file=credentials_file, scopes=scopes ) - google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes, default_scopes=None) + # TODO: remove if/else once google-auth >= 1.25.0 is required + if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + 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) @@ -496,6 +543,7 @@ def test_create_channel_with_credentials_file_and_scopes(load_credentials_from_f @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): @@ -510,7 +558,12 @@ def test_create_channel_with_credentials_file_and_default_scopes(load_credential target, credentials_file=credentials_file, scopes=scopes, default_scopes=default_scopes ) - load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes, default_scopes=default_scopes) + # TODO: remove if/else once google-auth >= 1.25.0 is required + if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes, default_scopes=default_scopes) + else: + 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) @@ -531,7 +584,12 @@ def test_create_channel_with_grpc_gcp(grpc_gcp_secure_channel): grpc_helpers.create_channel(target, credentials=credentials, scopes=scopes) grpc_gcp_secure_channel.assert_called() - credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None) + + # TODO: remove if/else once google-auth >= 1.25.0 is required + if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + 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") @@ -547,7 +605,7 @@ def test_create_channel_without_grpc_gcp(grpc_secure_channel): grpc_secure_channel.assert_called() # TODO: remove if/else once google-auth >= 1.25.0 is required - if pkg_resources.get_distribution("google-auth").version >= "1.25.0": + if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): credentials.with_scopes.assert_called_once_with(scopes, default_scopes=None) else: credentials.with_scopes.assert_called_once_with(scopes) From 90185002e59cdeb6808ee6fa70a10c9367f53bb8 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 5 Feb 2021 01:32:45 +0000 Subject: [PATCH 12/12] fix: get google_auth version using packaging --- google/api_core/grpc_helpers.py | 40 ++++++--- setup.py | 1 + testing/constraints-3.6.txt | 1 + tests/asyncio/test_grpc_helpers_async.py | 108 +++++++++++++++++------ tests/unit/test_grpc_helpers.py | 82 ++++++++--------- 5 files changed, 153 insertions(+), 79 deletions(-) diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py index 1522d866..5937f186 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) @@ -216,37 +232,37 @@ def _create_composite_credentials( ) if credentials_file: - # TODO: remove this try/except once google-auth >= 1.25.0 is required - try: + # 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 ) - except TypeError: + else: credentials, _ = google.auth.load_credentials_from_file( credentials_file, scopes=scopes or default_scopes, ) elif credentials: - # TODO: remove this try/except once google-auth >= 1.25.0 is required - try: + # 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 ) - except TypeError: + else: credentials = google.auth.credentials.with_scopes_if_required( credentials, scopes=scopes or default_scopes, ) else: - # TODO: remove this try/except once google-auth >= 1.25.0 is required - try: + # 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) - except TypeError: + else: credentials, _ = google.auth.default(scopes=scopes or default_scopes) if quota_project_id and isinstance(credentials, google.auth.credentials.CredentialsWithQuotaProject): @@ -256,12 +272,12 @@ def _create_composite_credentials( # Create the metadata plugin for inserting the authorization header. - # TODO: remove this try/except once google-auth >= 1.25.0 is required - try: + # 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, ) - except TypeError: + else: metadata_plugin = google.auth.transport.grpc.AuthMetadataPlugin( credentials, request ) diff --git a/setup.py b/setup.py index 30adb954..5de5aafb 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 2d498173..1fcd1934 100644 --- a/testing/constraints-3.6.txt +++ b/testing/constraints-3.6.txt @@ -10,6 +10,7 @@ protobuf==3.12.0 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 f0029831..3461cbe8 100644 --- a/tests/asyncio/test_grpc_helpers_async.py +++ b/tests/asyncio/test_grpc_helpers_async.py @@ -15,10 +15,10 @@ import grpc from grpc.experimental import aio import mock -import pkg_resources 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 @@ -264,6 +264,7 @@ def test_wrap_errors_streaming(wrap_stream_errors): @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") @@ -274,18 +275,25 @@ def test_create_channel_implicit(grpc_secure_channel, default, composite_creds_c channel = grpc_helpers_async.create_channel(target) assert channel is grpc_secure_channel.return_value - default.assert_called_once_with(scopes=None, default_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") +@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") @@ -297,8 +305,15 @@ def test_create_channel_implicit_with_default_host(grpc_secure_channel, default, channel = grpc_helpers_async.create_channel(target, default_host=default_host) assert channel is grpc_secure_channel.return_value - 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) + + # 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) @@ -317,7 +332,12 @@ def test_create_channel_implicit_with_ssl_creds( grpc_helpers_async.create_channel(target, ssl_credentials=ssl_creds) - default.assert_called_once_with(scopes=None, default_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) @@ -326,6 +346,7 @@ def test_create_channel_implicit_with_ssl_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") @@ -338,13 +359,20 @@ def test_create_channel_implicit_with_scopes( 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"], default_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=["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") @@ -354,10 +382,16 @@ def test_create_channel_implicit_with_default_scopes( target = "example.com:443" composite_creds = composite_creds_call.return_value - channel = grpc_helpers_async.create_channel(target, scopes=["one", "two"], default_scopes=["three", "four"]) + channel = grpc_helpers_async.create_channel(target, default_scopes=["three", "four"]) assert channel is grpc_secure_channel.return_value - default.assert_called_once_with(scopes=["one", "two"], default_scopes=["three", "four"]) + + # 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) @@ -375,7 +409,7 @@ def test_create_channel_explicit_with_duplicate_credentials(): @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" @@ -383,7 +417,12 @@ def test_create_channel_explicit(grpc_secure_channel, auth_creds, composite_cred channel = grpc_helpers_async.create_channel(target, credentials=mock.sentinel.credentials) - auth_creds.assert_called_once_with(mock.sentinel.credentials, scopes=None, default_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: + 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) @@ -401,9 +440,8 @@ def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_cal channel = grpc_helpers_async.create_channel( target, credentials=credentials, scopes=scopes ) - - # TODO: remove if/else once google-auth >= 1.25.0 is required - if pkg_resources.get_distribution("google-auth").version >= "1.25.0": + # 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) @@ -416,7 +454,6 @@ def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_cal @mock.patch("grpc.experimental.aio.secure_channel") def test_create_channel_explicit_default_scopes(grpc_secure_channel, composite_creds_call): target = "example.com:443" - scopes = ["1", "2"] default_scopes = ["3", "4"] composite_creds = composite_creds_call.return_value @@ -424,14 +461,14 @@ def test_create_channel_explicit_default_scopes(grpc_secure_channel, composite_c credentials.requires_scopes = True channel = grpc_helpers_async.create_channel( - target, credentials=credentials, scopes=scopes, default_scopes=default_scopes + target, credentials=credentials, default_scopes=default_scopes ) - # TODO: remove if/else once google-auth >= 1.25.0 is required - if pkg_resources.get_distribution("google-auth").version >= "1.25.0": - credentials.with_scopes.assert_called_once_with(scopes, 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) + 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) @@ -458,6 +495,7 @@ def test_create_channel_explicit_with_quota_project(grpc_secure_channel, composi @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): @@ -470,7 +508,12 @@ def test_create_channnel_with_credentials_file(load_credentials_from_file, grpc_ target, credentials_file=credentials_file ) - google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=None, default_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) @@ -479,6 +522,7 @@ def test_create_channnel_with_credentials_file(load_credentials_from_file, grpc_ @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): @@ -492,7 +536,12 @@ def test_create_channel_with_credentials_file_and_scopes(load_credentials_from_f target, credentials_file=credentials_file, scopes=scopes ) - google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes, default_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=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) @@ -501,21 +550,26 @@ def test_create_channel_with_credentials_file_and_scopes(load_credentials_from_f @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" - scopes = ["1", "2"] 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, scopes=scopes, default_scopes=default_scopes + target, credentials_file=credentials_file, default_scopes=default_scopes ) - google.auth.load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes, 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) @@ -532,8 +586,8 @@ def test_create_channel_without_grpc_gcp(grpc_secure_channel): grpc_helpers_async.create_channel(target, credentials=credentials, scopes=scopes) grpc_secure_channel.assert_called() - # TODO: remove if/else once google-auth >= 1.25.0 is required - if pkg_resources.get_distribution("google-auth").version >= "1.25.0": + # 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) diff --git a/tests/unit/test_grpc_helpers.py b/tests/unit/test_grpc_helpers.py index 880d2771..4e0ab806 100644 --- a/tests/unit/test_grpc_helpers.py +++ b/tests/unit/test_grpc_helpers.py @@ -14,8 +14,6 @@ import grpc import mock -from packaging import version -import pkg_resources import pytest from google.api_core import exceptions @@ -236,8 +234,8 @@ def test_create_channel_implicit(grpc_secure_channel, default, composite_creds_c assert channel is grpc_secure_channel.return_value - # TODO: remove if/else once google-auth >= 1.25.0 is required - if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + # 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) @@ -270,14 +268,16 @@ def test_create_channel_implicit_with_default_host(grpc_secure_channel, default, assert channel is grpc_secure_channel.return_value - # TODO: remove if/else once google-auth >= 1.25.0 is required - if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + # 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: @@ -300,8 +300,8 @@ def test_create_channel_implicit_with_ssl_creds( grpc_helpers.create_channel(target, ssl_credentials=ssl_creds) - # TODO: remove if/else once google-auth >= 1.25.0 is required - if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + # 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) @@ -331,8 +331,8 @@ def test_create_channel_implicit_with_scopes( assert channel is grpc_secure_channel.return_value - # TODO: remove if/else once google-auth >= 1.25.0 is required - if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + # 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"]) @@ -356,15 +356,15 @@ def test_create_channel_implicit_with_default_scopes( target = "example.com:443" composite_creds = composite_creds_call.return_value - channel = grpc_helpers.create_channel(target, scopes=["one", "two"], default_scopes=["three", "four"]) + channel = grpc_helpers.create_channel(target, default_scopes=["three", "four"]) assert channel is grpc_secure_channel.return_value - # TODO: remove if/else once google-auth >= 1.25.0 is required - if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): - default.assert_called_once_with(scopes=["one", "two"], default_scopes=["three", "four"]) + # 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=["one", "two"]) + 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) @@ -384,7 +384,7 @@ def test_create_channel_explicit_with_duplicate_credentials(): @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" @@ -392,7 +392,11 @@ def test_create_channel_explicit(grpc_secure_channel, auth_creds, composite_cred channel = grpc_helpers.create_channel(target, credentials=mock.sentinel.credentials) - auth_creds.assert_called_once_with(mock.sentinel.credentials, scopes=None, default_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: + 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) @@ -414,8 +418,8 @@ def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_cal target, credentials=credentials, scopes=scopes ) - # TODO: remove if/else once google-auth >= 1.25.0 is required - if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + # 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) @@ -431,7 +435,6 @@ def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_cal @mock.patch("grpc.secure_channel") def test_create_channel_explicit_default_scopes(grpc_secure_channel, composite_creds_call): target = "example.com:443" - scopes = ["1", "2"] default_scopes = ["3", "4"] composite_creds = composite_creds_call.return_value @@ -439,14 +442,14 @@ def test_create_channel_explicit_default_scopes(grpc_secure_channel, composite_c credentials.requires_scopes = True channel = grpc_helpers.create_channel( - target, credentials=credentials, scopes=scopes, default_scopes=default_scopes + target, credentials=credentials, default_scopes=default_scopes ) - # TODO: remove if/else once google-auth >= 1.25.0 is required - if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): - credentials.with_scopes.assert_called_once_with(scopes, 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) + credentials.with_scopes.assert_called_once_with(scopes=default_scopes) assert channel is grpc_secure_channel.return_value if grpc_helpers.HAS_GRPC_GCP: @@ -495,8 +498,8 @@ def test_create_channel_with_credentials_file(load_credentials_from_file, grpc_s target, credentials_file=credentials_file ) - # TODO: remove if/else once google-auth >= 1.25.0 is required - if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + # 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) @@ -526,8 +529,8 @@ def test_create_channel_with_credentials_file_and_scopes(load_credentials_from_f target, credentials_file=credentials_file, scopes=scopes ) - # TODO: remove if/else once google-auth >= 1.25.0 is required - if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + # 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) @@ -548,21 +551,20 @@ def test_create_channel_with_credentials_file_and_scopes(load_credentials_from_f ) def test_create_channel_with_credentials_file_and_default_scopes(load_credentials_from_file, grpc_secure_channel, composite_creds_call): target = "example.com:443" - scopes = ["1", "2"] 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, scopes=scopes, default_scopes=default_scopes + target, credentials_file=credentials_file, default_scopes=default_scopes ) - # TODO: remove if/else once google-auth >= 1.25.0 is required - if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): - load_credentials_from_file.assert_called_once_with(credentials_file, scopes=scopes, 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=scopes) + 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: @@ -585,8 +587,8 @@ def test_create_channel_with_grpc_gcp(grpc_gcp_secure_channel): grpc_helpers.create_channel(target, credentials=credentials, scopes=scopes) grpc_gcp_secure_channel.assert_called() - # TODO: remove if/else once google-auth >= 1.25.0 is required - if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + # 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) @@ -604,8 +606,8 @@ def test_create_channel_without_grpc_gcp(grpc_secure_channel): grpc_helpers.create_channel(target, credentials=credentials, scopes=scopes) grpc_secure_channel.assert_called() - # TODO: remove if/else once google-auth >= 1.25.0 is required - if version.Version(pkg_resources.get_distribution("google-auth").version) >= version.Version("1.25.0"): + # 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)