Skip to content

Commit b47cd25

Browse files
committed
chore(generator): gapic generator centralization mtls
1 parent a12aeef commit b47cd25

28 files changed

Lines changed: 337 additions & 2616 deletions

File tree

packages/gapic-generator/gapic/generator/generator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def get_response(self, api_schema: api.API, opts: Options) -> CodeGeneratorRespo
120120
for template_name in client_templates:
121121
# Quick check: Skip "private" templates.
122122
filename = template_name.split("/")[-1]
123-
if filename.startswith("_") and filename != "__init__.py.j2":
123+
if filename.startswith("_") and filename not in ("__init__.py.j2", "_compat.py.j2"):
124124
continue
125125

126126
# Append to the output files dictionary.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# {% include '_license.j2' %}
2+
3+
import os
4+
from typing import Optional, Callable, Tuple, Union
5+
from google.auth.exceptions import MutualTLSChannelError
6+
7+
try:
8+
from google.api_core.gapic_v1.client_utils import (
9+
use_client_cert_effective,
10+
get_client_cert_source,
11+
read_environment_variables,
12+
)
13+
except ImportError:
14+
from google.auth.transport import mtls # type: ignore
15+
16+
# TODO: Remove these fallbacks when google-api-core >= 2.18.0 is the minimum required version.
17+
18+
def use_client_cert_effective() -> bool:
19+
"""Returns whether client certificate should be used for mTLS."""
20+
if hasattr(mtls, "should_use_client_cert"):
21+
return mtls.should_use_client_cert()
22+
else:
23+
use_client_cert_str = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false").lower()
24+
if use_client_cert_str not in ("true", "false"):
25+
raise ValueError(
26+
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
27+
" either `true` or `false`"
28+
)
29+
return use_client_cert_str == "true"
30+
31+
def get_client_cert_source(
32+
provided_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]],
33+
use_cert_flag: bool,
34+
) -> Optional[Callable[[], Tuple[bytes, bytes]]]:
35+
"""Return the client cert source to be used by the client."""
36+
client_cert_source = None
37+
if use_cert_flag:
38+
if provided_cert_source:
39+
client_cert_source = provided_cert_source
40+
elif (
41+
hasattr(mtls, "has_default_client_cert_source")
42+
and mtls.has_default_client_cert_source()
43+
):
44+
client_cert_source = mtls.default_client_cert_source()
45+
else:
46+
raise ValueError(
47+
"Client certificate is required for mTLS, but no client certificate source was provided or found."
48+
)
49+
return client_cert_source
50+
51+
def read_environment_variables() -> Tuple[bool, str, Optional[str]]:
52+
"""Returns the environment variables used by the client."""
53+
use_client_cert = use_client_cert_effective()
54+
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower()
55+
universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN")
56+
if use_mtls_endpoint not in ("auto", "never", "always"):
57+
raise MutualTLSChannelError(
58+
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` "
59+
"must be `never`, `auto` or `always`"
60+
)
61+
return use_client_cert, use_mtls_endpoint, universe_domain_env

packages/gapic-generator/gapic/templates/%namespace/%name_%version/%sub/services/%service/client.py.j2

Lines changed: 11 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ from google.api_core import exceptions as core_exceptions
3030
from google.api_core import extended_operation
3131
{% endif %}
3232
from google.api_core import gapic_v1
33+
from {{package_path}} import _compat as client_utils
3334
from google.api_core import retry as retries
3435
from google.auth import credentials as ga_credentials # type: ignore
3536
from google.auth.transport import mtls # type: ignore
@@ -189,30 +190,9 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
189190
_DEFAULT_UNIVERSE = "googleapis.com"
190191

191192
@staticmethod
192-
def _use_client_cert_effective():
193-
"""Returns whether client certificate should be used for mTLS if the
194-
google-auth version supports should_use_client_cert automatic mTLS enablement.
195-
196-
Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var.
197-
198-
Returns:
199-
bool: whether client certificate should be used for mTLS
200-
Raises:
201-
ValueError: (If using a version of google-auth without should_use_client_cert and
202-
GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.)
203-
"""
204-
# check if google-auth version supports should_use_client_cert for automatic mTLS enablement
205-
if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER
206-
return mtls.should_use_client_cert()
207-
else: # pragma: NO COVER
208-
# if unsupported, fallback to reading from env var
209-
use_client_cert_str = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false").lower()
210-
if use_client_cert_str not in ("true", "false"):
211-
raise ValueError(
212-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
213-
" either `true` or `false`"
214-
)
215-
return use_client_cert_str == "true"
193+
def _use_client_cert_effective() -> bool:
194+
"""Returns whether client certificate should be used for mTLS."""
195+
return client_utils.use_client_cert_effective()
216196

217197
@classmethod
218198
def from_service_account_info(cls, info: dict, *args, **kwargs):
@@ -352,44 +332,12 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
352332
return api_endpoint, client_cert_source
353333

354334
@staticmethod
355-
def _read_environment_variables():
356-
"""Returns the environment variables used by the client.
357-
358-
Returns:
359-
Tuple[bool, str, str]: returns the GOOGLE_API_USE_CLIENT_CERTIFICATE,
360-
GOOGLE_API_USE_MTLS_ENDPOINT, and GOOGLE_CLOUD_UNIVERSE_DOMAIN environment variables.
361-
362-
Raises:
363-
ValueError: If GOOGLE_API_USE_CLIENT_CERTIFICATE is not
364-
any of ["true", "false"].
365-
google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT
366-
is not any of ["auto", "never", "always"].
367-
"""
368-
use_client_cert = {{ service.client_name }}._use_client_cert_effective()
369-
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower()
370-
universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN")
371-
if use_mtls_endpoint not in ("auto", "never", "always"):
372-
raise MutualTLSChannelError("Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`")
373-
return use_client_cert, use_mtls_endpoint, universe_domain_env
374-
375-
@staticmethod
376-
def _get_client_cert_source(provided_cert_source, use_cert_flag):
377-
"""Return the client cert source to be used by the client.
378-
379-
Args:
380-
provided_cert_source (bytes): The client certificate source provided.
381-
use_cert_flag (bool): A flag indicating whether to use the client certificate.
382-
383-
Returns:
384-
bytes or None: The client cert source to be used by the client.
385-
"""
386-
client_cert_source = None
387-
if use_cert_flag:
388-
if provided_cert_source:
389-
client_cert_source = provided_cert_source
390-
elif mtls.has_default_client_cert_source():
391-
client_cert_source = mtls.default_client_cert_source()
392-
return client_cert_source
335+
def _get_client_cert_source(
336+
provided_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]],
337+
use_cert_flag: bool,
338+
) -> Optional[Callable[[], Tuple[bytes, bytes]]]:
339+
"""Return the client cert source to be used by the client."""
340+
return client_utils.get_client_cert_source(provided_cert_source, use_cert_flag)
393341

394342
@staticmethod
395343
def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str:
@@ -597,7 +545,7 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
597545

598546
universe_domain_opt = getattr(self._client_options, 'universe_domain', None)
599547

600-
self._use_client_cert, self._use_mtls_endpoint, self._universe_domain_env = {{ service.client_name }}._read_environment_variables()
548+
self._use_client_cert, self._use_mtls_endpoint, self._universe_domain_env = client_utils.read_environment_variables()
601549
self._client_cert_source = {{ service.client_name }}._get_client_cert_source(self._client_options.client_cert_source, self._use_client_cert)
602550
self._universe_domain = {{ service.client_name }}._get_universe_domain(universe_domain_opt, self._universe_domain_env)
603551
self._api_endpoint: str = "" # updated below, depending on `transport`

0 commit comments

Comments
 (0)