| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -13,7 +13,7 @@ | |||
| 13 | 13 | # limitations under the License. | |
| 14 | 14 | ||
| 15 | 15 | """Helpers for :mod:`grpc`.""" | |
| 16 | - from typing import Generic, TypeVar, Iterator | ||
| 16 | + from typing import Generic, Iterator, Optional, TypeVar | ||
| 17 | 17 | ||
| 18 | 18 | import collections | |
| 19 | 19 | import functools | |
@@ -271,11 +271,24 @@ def _create_composite_credentials( | |||
| 271 | 271 | # Create a set of grpc.CallCredentials using the metadata plugin. | |
| 272 | 272 | google_auth_credentials = grpc.metadata_call_credentials(metadata_plugin) | |
| 273 | 273 | ||
| 274 | - if ssl_credentials is None: | ||
| 275 | - ssl_credentials = grpc.ssl_channel_credentials() | ||
| 274 | + # if `ssl_credentials` is set, use `grpc.composite_channel_credentials` instead of | ||
| 275 | + # `grpc.compute_engine_channel_credentials` as the former supports passing | ||
| 276 | + # `ssl_credentials` via `channel_credentials` which is needed for mTLS. | ||
| 277 | + if ssl_credentials: | ||
| 278 | + # Combine the ssl credentials and the authorization credentials. | ||
| 279 | + # See https://grpc.github.io/grpc/python/grpc.html#grpc.composite_channel_credentials | ||
| 280 | + return grpc.composite_channel_credentials( | ||
| 281 | + ssl_credentials, google_auth_credentials | ||
| 282 | + ) | ||
| 283 | + else: | ||
| 284 | + # Use grpc.compute_engine_channel_credentials in order to support Direct Path. | ||
| 285 | + # See https://grpc.github.io/grpc/python/grpc.html#grpc.compute_engine_channel_credentials | ||
| 276 | 286 | ||
| 277 | - # Combine the ssl credentials and the authorization credentials. | ||
| 278 | - return grpc.composite_channel_credentials(ssl_credentials, google_auth_credentials) | ||
| 287 | + # TODO(<insert bug to github issue>): Although `grpc.compute_engine_channel_credentials` | ||
| 288 | + # returns channel credentials outside of GCE, we should determine if there is a way to | ||
| 289 | + # reliably detect when the client is in a GCE environment so that | ||
| 290 | + # `grpc.compute_engine_channel_credentials` is not called outside of GCE. | ||
| 291 | + return grpc.compute_engine_channel_credentials(google_auth_credentials) | ||
| 279 | 292 | ||
| 280 | 293 | ||
| 281 | 294 | def create_channel( | |
@@ -288,6 +301,7 @@ def create_channel( | |||
| 288 | 301 | default_scopes=None, | |
| 289 | 302 | default_host=None, | |
| 290 | 303 | compression=None, | |
| 304 | + attempt_direct_path: Optional[bool] = None, | ||
| 291 | 305 | **kwargs, | |
| 292 | 306 | ): | |
| 293 | 307 | """Create a secure channel with credentials. | |
@@ -311,6 +325,16 @@ def create_channel( | |||
| 311 | 325 | default_host (str): The default endpoint. e.g., "pubsub.googleapis.com". | |
| 312 | 326 | compression (grpc.Compression): An optional value indicating the | |
| 313 | 327 | compression method to be used over the lifetime of the channel. | |
| 328 | + attempt_direct_path (Optional[bool]): If set, Direct Path will be attempted when | ||
| 329 | + the request is made. Direct Path provides a proxyless connection which | ||
| 330 | + increases the available throughput, reduces latency, and increases | ||
| 331 | + reliability. Outside of GCE, the direct path request may fallback | ||
| 332 | + to DNS if this is configured by the Service. This argument should only | ||
| 333 | + be set in a GCE environment and for Services that are known to support Direct Path. | ||
| 334 | + If a `ServiceUnavailable` response is received when the request is sent, it is | ||
| 335 | + recommended that the client repeat the request with `attempt_direct_path` set to `False` | ||
| 336 | + as the Service may not support Direct Path. Using `ssl_credentials` with `attempt_direct_path` | ||
| 337 | + set to `True` will result in `ValueError` as it is not yet supported. | ||
| 314 | 338 | kwargs: Additional key-word args passed to | |
| 315 | 339 | :func:`grpc_gcp.secure_channel` or :func:`grpc.secure_channel`. | |
| 316 | 340 | Note: `grpc_gcp` is only supported in environments with protobuf < 4.0.0. | |
@@ -320,8 +344,15 @@ def create_channel( | |||
| 320 | 344 | ||
| 321 | 345 | Raises: | |
| 322 | 346 | google.api_core.DuplicateCredentialArgs: If both a credentials object and credentials_file are passed. | |
| 347 | + ValueError: If `ssl_credentials` is set and `attempt_direct_path` is set to `True`. | ||
| 323 | 348 | """ | |
| 324 | 349 | ||
| 350 | + # If `ssl_credentials` is set and `attempt_direct_path` is set to `True`, | ||
| 351 | + # raise ValueError as this is not yet supported. | ||
| 352 | + # TODO(<insert bug to github issue>): Add link to Github Issue | ||
| 353 | + if ssl_credentials is not None and attempt_direct_path: | ||
| 354 | + raise ValueError("Using ssl_credentials with Direct Path is not supported") | ||
| 355 | + | ||
| 325 | 356 | composite_credentials = _create_composite_credentials( | |
| 326 | 357 | credentials=credentials, | |
| 327 | 358 | credentials_file=credentials_file, | |
@@ -332,17 +363,53 @@ def create_channel( | |||
| 332 | 363 | default_host=default_host, | |
| 333 | 364 | ) | |
| 334 | 365 | ||
| 366 | + # Note that grpcio-gcp is deprecated | ||
| 335 | 367 | if HAS_GRPC_GCP: # pragma: NO COVER | |
| 336 | 368 | if compression is not None and compression != grpc.Compression.NoCompression: | |
| 337 | 369 | _LOGGER.debug( | |
| 338 | 370 | "Compression argument is being ignored for grpc_gcp.secure_channel creation." | |
| 339 | 371 | ) | |
| 372 | + if attempt_direct_path: | ||
| 373 | + warnings.warn( | ||
| 374 | + """The `attempt_direct_path` argument is ignored for grpc_gcp.secure_channel creation.""", | ||
| 375 | + DeprecationWarning, | ||
| 376 | + ) | ||
| 340 | 377 | return grpc_gcp.secure_channel(target, composite_credentials, **kwargs) | |
| 378 | + | ||
| 379 | + if attempt_direct_path: | ||
| 380 | + target = _modify_target_for_direct_path(target) | ||
| 381 | + | ||
| 341 | 382 | return grpc.secure_channel( | |
| 342 | 383 | target, composite_credentials, compression=compression, **kwargs | |
| 343 | 384 | ) | |
| 344 | 385 | ||
| 345 | 386 | ||
| 387 | + def _modify_target_for_direct_path(target: str) -> str: | ||
| 388 | + """Create a secure channel with credentials. | ||
| 389 | + | ||
| 390 | + Args: | ||
| 391 | + target (str): The target service address in the format 'hostname:port', 'dns://hostname' or other | ||
| 392 | + compatible format. | ||
| 393 | + | ||
| 394 | + Returns: | ||
| 395 | + target (str): The target service address which is converted into a format compatible with Direct Path. | ||
| 396 | + If the target contains `dns:///` or does not have contain `:///`, the target will be converted in | ||
| 397 | + a format compatible with Direct Path, otherwise the original target will be returned. | ||
| 398 | + """ | ||
| 399 | + | ||
| 400 | + dns_prefix = "dns:///" | ||
| 401 | + # Remove "dns:///" if `attempt_direct_path` is set to True as | ||
| 402 | + # the Direct Path prefix `google-c2p:///` will be used instead. | ||
| 403 | + target = target.replace(dns_prefix, "") | ||
| 404 | + | ||
| 405 | + direct_path_prefix = ":///" | ||
| 406 | + if direct_path_prefix not in target: | ||
| 407 | + target_without_port = target.split(":")[0] | ||
| 408 | + # Modify the target to use Direct Path by adding the `google-c2p:///` prefix | ||
| 409 | + target = f"google-c2p{direct_path_prefix}{target_without_port}" | ||
| 410 | + return target | ||
| 411 | + | ||
| 412 | + | ||
| 346 | 413 | _MethodCall = collections.namedtuple( | |
| 347 | 414 | "_MethodCall", ("request", "timeout", "metadata", "credentials", "compression") | |
| 348 | 415 | ) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -21,7 +21,7 @@ | |||
| 21 | 21 | import asyncio | |
| 22 | 22 | import functools | |
| 23 | 23 | ||
| 24 | - from typing import Generic, Iterator, AsyncGenerator, TypeVar | ||
| 24 | + from typing import AsyncGenerator, Generic, Iterator, Optional, TypeVar | ||
| 25 | 25 | ||
| 26 | 26 | import grpc | |
| 27 | 27 | from grpc import aio | |
@@ -223,6 +223,7 @@ def create_channel( | |||
| 223 | 223 | default_scopes=None, | |
| 224 | 224 | default_host=None, | |
| 225 | 225 | compression=None, | |
| 226 | + attempt_direct_path: Optional[bool] = None, | ||
| 226 | 227 | **kwargs | |
| 227 | 228 | ): | |
| 228 | 229 | """Create an AsyncIO secure channel with credentials. | |
@@ -246,15 +247,32 @@ def create_channel( | |||
| 246 | 247 | default_host (str): The default endpoint. e.g., "pubsub.googleapis.com". | |
| 247 | 248 | compression (grpc.Compression): An optional value indicating the | |
| 248 | 249 | compression method to be used over the lifetime of the channel. | |
| 250 | + attempt_direct_path (Optional[bool]): If set, Direct Path will be attempted when | ||
| 251 | + the request is made. Direct Path provides a proxyless connection which | ||
| 252 | + increases the available throughput, reduces latency, and increases | ||
| 253 | + reliability. Outside of GCE, the direct path request may fallback | ||
| 254 | + to DNS if this is configured by the Service. This argument should only | ||
| 255 | + be set in a GCE environment and for Services that are known to support Direct Path. | ||
| 256 | + If a `ServiceUnavailable` response is received when the request is sent, it is | ||
| 257 | + recommended that the client repeat the request with `attempt_direct_path` set to `False` | ||
| 258 | + as the Service may not support Direct Path. Using `ssl_credentials` with `attempt_direct_path` | ||
| 259 | + set to `True` will result in `ValueError` as it is not yet supported. | ||
| 249 | 260 | kwargs: Additional key-word args passed to :func:`aio.secure_channel`. | |
| 250 | 261 | ||
| 251 | 262 | Returns: | |
| 252 | 263 | aio.Channel: The created channel. | |
| 253 | 264 | ||
| 254 | 265 | Raises: | |
| 255 | 266 | google.api_core.DuplicateCredentialArgs: If both a credentials object and credentials_file are passed. | |
| 267 | + ValueError: If `ssl_credentials` is set and `attempt_direct_path` is set to `True`. | ||
| 256 | 268 | """ | |
| 257 | 269 | ||
| 270 | + # If `ssl_credentials` is set and `attempt_direct_path` is set to `True`, | ||
| 271 | + # raise ValueError as this is not yet supported. | ||
| 272 | + # TODO(<insert bug to github issue>): Add link to Github Issue | ||
| 273 | + if ssl_credentials is not None and attempt_direct_path: | ||
| 274 | + raise ValueError("Using ssl_credentials with Direct Path is not supported") | ||
| 275 | + | ||
| 258 | 276 | composite_credentials = grpc_helpers._create_composite_credentials( | |
| 259 | 277 | credentials=credentials, | |
| 260 | 278 | credentials_file=credentials_file, | |
@@ -265,6 +283,9 @@ def create_channel( | |||
| 265 | 283 | default_host=default_host, | |
| 266 | 284 | ) | |
| 267 | 285 | ||
| 286 | + if attempt_direct_path: | ||
| 287 | + target = grpc_helpers._modify_target_for_direct_path(target) | ||
| 288 | + | ||
| 268 | 289 | return aio.secure_channel( | |
| 269 | 290 | target, composite_credentials, compression=compression, **kwargs | |
| 270 | 291 | ) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -298,7 +298,7 @@ def test_wrap_errors_streaming(wrap_stream_errors): | |||
| 298 | 298 | wrap_stream_errors.assert_called_once_with(callable_) | |
| 299 | 299 | ||
| 300 | 300 | ||
| 301 | - @mock.patch("grpc.composite_channel_credentials") | ||
| 301 | + @mock.patch("grpc.compute_engine_channel_credentials") | ||
| 302 | 302 | @mock.patch( | |
| 303 | 303 | "google.auth.default", | |
| 304 | 304 | autospec=True, | |
@@ -325,7 +325,7 @@ def test_create_channel_implicit(grpc_secure_channel, default, composite_creds_c | |||
| 325 | 325 | autospec=True, | |
| 326 | 326 | return_value=mock.sentinel.Request, | |
| 327 | 327 | ) | |
| 328 | - @mock.patch("grpc.composite_channel_credentials") | ||
| 328 | + @mock.patch("grpc.compute_engine_channel_credentials") | ||
| 329 | 329 | @mock.patch( | |
| 330 | 330 | "google.auth.default", | |
| 331 | 331 | autospec=True, | |
@@ -375,7 +375,18 @@ def test_create_channel_implicit_with_ssl_creds( | |||
| 375 | 375 | ) | |
| 376 | 376 | ||
| 377 | 377 | ||
| 378 | - @mock.patch("grpc.composite_channel_credentials") | ||
| 378 | + def test_create_channel_implicit_with_ssl_creds_direct_path(): | ||
| 379 | + target = "example.com:443" | ||
| 380 | + ssl_creds = grpc.ssl_channel_credentials() | ||
| 381 | + with pytest.raises( | ||
| 382 | + ValueError, match="Using ssl_credentials with Direct Path is not supported" | ||
| 383 | + ): | ||
| 384 | + grpc_helpers_async.create_channel( | ||
| 385 | + target, ssl_credentials=ssl_creds, attempt_direct_path=True | ||
| 386 | + ) | ||
| 387 | + | ||
| 388 | + | ||
| 389 | + @mock.patch("grpc.compute_engine_channel_credentials") | ||
| 379 | 390 | @mock.patch( | |
| 380 | 391 | "google.auth.default", | |
| 381 | 392 | autospec=True, | |
@@ -398,7 +409,7 @@ def test_create_channel_implicit_with_scopes( | |||
| 398 | 409 | ) | |
| 399 | 410 | ||
| 400 | 411 | ||
| 401 | - @mock.patch("grpc.composite_channel_credentials") | ||
| 412 | + @mock.patch("grpc.compute_engine_channel_credentials") | ||
| 402 | 413 | @mock.patch( | |
| 403 | 414 | "google.auth.default", | |
| 404 | 415 | autospec=True, | |
@@ -436,7 +447,7 @@ def test_create_channel_explicit_with_duplicate_credentials(): | |||
| 436 | 447 | assert "mutually exclusive" in str(excinfo.value) | |
| 437 | 448 | ||
| 438 | 449 | ||
| 439 | - @mock.patch("grpc.composite_channel_credentials") | ||
| 450 | + @mock.patch("grpc.compute_engine_channel_credentials") | ||
| 440 | 451 | @mock.patch("google.auth.credentials.with_scopes_if_required", autospec=True) | |
| 441 | 452 | @mock.patch("grpc.aio.secure_channel") | |
| 442 | 453 | def test_create_channel_explicit(grpc_secure_channel, auth_creds, composite_creds_call): | |
@@ -456,7 +467,7 @@ def test_create_channel_explicit(grpc_secure_channel, auth_creds, composite_cred | |||
| 456 | 467 | ) | |
| 457 | 468 | ||
| 458 | 469 | ||
| 459 | - @mock.patch("grpc.composite_channel_credentials") | ||
| 470 | + @mock.patch("grpc.compute_engine_channel_credentials") | ||
| 460 | 471 | @mock.patch("grpc.aio.secure_channel") | |
| 461 | 472 | def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_call): | |
| 462 | 473 | target = "example.com:443" | |
@@ -480,7 +491,7 @@ def test_create_channel_explicit_scoped(grpc_secure_channel, composite_creds_cal | |||
| 480 | 491 | ) | |
| 481 | 492 | ||
| 482 | 493 | ||
| 483 | - @mock.patch("grpc.composite_channel_credentials") | ||
| 494 | + @mock.patch("grpc.compute_engine_channel_credentials") | ||
| 484 | 495 | @mock.patch("grpc.aio.secure_channel") | |
| 485 | 496 | def test_create_channel_explicit_default_scopes( | |
| 486 | 497 | grpc_secure_channel, composite_creds_call | |
@@ -508,7 +519,7 @@ def test_create_channel_explicit_default_scopes( | |||
| 508 | 519 | ) | |
| 509 | 520 | ||
| 510 | 521 | ||
| 511 | - @mock.patch("grpc.composite_channel_credentials") | ||
| 522 | + @mock.patch("grpc.compute_engine_channel_credentials") | ||
| 512 | 523 | @mock.patch("grpc.aio.secure_channel") | |
| 513 | 524 | def test_create_channel_explicit_with_quota_project( | |
| 514 | 525 | grpc_secure_channel, composite_creds_call | |
@@ -531,7 +542,7 @@ def test_create_channel_explicit_with_quota_project( | |||
| 531 | 542 | ) | |
| 532 | 543 | ||
| 533 | 544 | ||
| 534 | - @mock.patch("grpc.composite_channel_credentials") | ||
| 545 | + @mock.patch("grpc.compute_engine_channel_credentials") | ||
| 535 | 546 | @mock.patch("grpc.aio.secure_channel") | |
| 536 | 547 | @mock.patch( | |
| 537 | 548 | "google.auth.load_credentials_from_file", | |
@@ -559,7 +570,7 @@ def test_create_channel_with_credentials_file( | |||
| 559 | 570 | ) | |
| 560 | 571 | ||
| 561 | 572 | ||
| 562 | - @mock.patch("grpc.composite_channel_credentials") | ||
| 573 | + @mock.patch("grpc.compute_engine_channel_credentials") | ||
| 563 | 574 | @mock.patch("grpc.aio.secure_channel") | |
| 564 | 575 | @mock.patch( | |
| 565 | 576 | "google.auth.load_credentials_from_file", | |
@@ -588,7 +599,7 @@ def test_create_channel_with_credentials_file_and_scopes( | |||
| 588 | 599 | ) | |
| 589 | 600 | ||
| 590 | 601 | ||
| 591 | - @mock.patch("grpc.composite_channel_credentials") | ||
| 602 | + @mock.patch("grpc.compute_engine_channel_credentials") | ||
| 592 | 603 | @mock.patch("grpc.aio.secure_channel") | |
| 593 | 604 | @mock.patch( | |
| 594 | 605 | "google.auth.load_credentials_from_file", | |
| Back | FazBrowse Home | New Git URL |
0 commit comments