Skip to content

Commit bbcc340

Browse files
committed
refactor(generator): use centralized mtls and client config helpers
1 parent 93c5270 commit bbcc340

26 files changed

Lines changed: 182 additions & 2612 deletions

File tree

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

Lines changed: 14 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ 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 google.api_core.gapic_v1 import client_cert
34+
from google.api_core.gapic_v1 import config_helpers
3335
from google.api_core import retry as retries
3436
from google.auth import credentials as ga_credentials # type: ignore
3537
from google.auth.transport import mtls # type: ignore
@@ -189,30 +191,9 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
189191
_DEFAULT_UNIVERSE = "googleapis.com"
190192

191193
@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"
194+
def _use_client_cert_effective() -> bool:
195+
"""Returns whether client certificate should be used for mTLS."""
196+
return client_cert.use_client_cert_effective()
216197

217198
@classmethod
218199
def from_service_account_info(cls, info: dict, *args, **kwargs):
@@ -352,44 +333,17 @@ class {{ service.client_name }}(metaclass={{ service.client_name }}Meta):
352333
return api_endpoint, client_cert_source
353334

354335
@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
336+
def _read_environment_variables() -> Tuple[bool, str, Optional[str]]:
337+
"""Returns the environment variables used by the client."""
338+
return config_helpers.read_environment_variables()
374339

375340
@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
341+
def _get_client_cert_source(
342+
provided_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]],
343+
use_cert_flag: bool,
344+
) -> Optional[Callable[[], Tuple[bytes, bytes]]]:
345+
"""Return the client cert source to be used by the client."""
346+
return client_cert.get_client_cert_source(provided_cert_source, use_cert_flag)
393347

394348
@staticmethod
395349
def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str:

packages/gapic-generator/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_%service.py.j2

Lines changed: 0 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -172,146 +172,6 @@ def test__get_default_mtls_endpoint():
172172
assert {{ service.client_name }}._get_default_mtls_endpoint(non_googleapi) == non_googleapi
173173
assert {{ service.client_name }}._get_default_mtls_endpoint(custom_endpoint) == custom_endpoint
174174

175-
def test__read_environment_variables():
176-
assert {{ service.client_name }}._read_environment_variables() == (False, "auto", None)
177-
178-
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
179-
assert {{ service.client_name }}._read_environment_variables() == (True, "auto", None)
180-
181-
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}):
182-
assert {{ service.client_name }}._read_environment_variables() == (False, "auto", None)
183-
184-
with mock.patch.dict(
185-
os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"}
186-
):
187-
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
188-
with pytest.raises(ValueError) as excinfo:
189-
{{ service.client_name }}._read_environment_variables()
190-
assert (
191-
str(excinfo.value)
192-
== "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
193-
)
194-
else:
195-
assert {{ service.client_name }}._read_environment_variables() == (
196-
False,
197-
"auto",
198-
None,
199-
)
200-
201-
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}):
202-
assert {{ service.client_name }}._read_environment_variables() == (False, "never", None)
203-
204-
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}):
205-
assert {{ service.client_name }}._read_environment_variables() == (False, "always", None)
206-
207-
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}):
208-
assert {{ service.client_name }}._read_environment_variables() == (False, "auto", None)
209-
210-
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}):
211-
with pytest.raises(MutualTLSChannelError) as excinfo:
212-
{{ service.client_name }}._read_environment_variables()
213-
assert str(excinfo.value) == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
214-
215-
with mock.patch.dict(os.environ, {"GOOGLE_CLOUD_UNIVERSE_DOMAIN": "foo.com"}):
216-
assert {{ service.client_name }}._read_environment_variables() == (False, "auto", "foo.com")
217-
218-
219-
def test_use_client_cert_effective():
220-
# Test case 1: Test when `should_use_client_cert` returns True.
221-
# We mock the `should_use_client_cert` function to simulate a scenario where
222-
# the google-auth library supports automatic mTLS and determines that a
223-
# client certificate should be used.
224-
if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
225-
with mock.patch("google.auth.transport.mtls.should_use_client_cert", return_value=True):
226-
assert {{ service.client_name }}._use_client_cert_effective() is True
227-
228-
# Test case 2: Test when `should_use_client_cert` returns False.
229-
# We mock the `should_use_client_cert` function to simulate a scenario where
230-
# the google-auth library supports automatic mTLS and determines that a
231-
# client certificate should NOT be used.
232-
if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
233-
with mock.patch("google.auth.transport.mtls.should_use_client_cert", return_value=False):
234-
assert {{ service.client_name }}._use_client_cert_effective() is False
235-
236-
# Test case 3: Test when `should_use_client_cert` is unavailable and the
237-
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true".
238-
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
239-
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}):
240-
assert {{ service.client_name }}._use_client_cert_effective() is True
241-
242-
# Test case 4: Test when `should_use_client_cert` is unavailable and the
243-
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "false".
244-
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
245-
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}):
246-
assert {{ service.client_name }}._use_client_cert_effective() is False
247-
248-
# Test case 5: Test when `should_use_client_cert` is unavailable and the
249-
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "True".
250-
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
251-
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "True"}):
252-
assert {{ service.client_name }}._use_client_cert_effective() is True
253-
254-
# Test case 6: Test when `should_use_client_cert` is unavailable and the
255-
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "False".
256-
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
257-
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "False"}):
258-
assert {{ service.client_name }}._use_client_cert_effective() is False
259-
260-
# Test case 7: Test when `should_use_client_cert` is unavailable and the
261-
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "TRUE".
262-
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
263-
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "TRUE"}):
264-
assert {{ service.client_name }}._use_client_cert_effective() is True
265-
266-
# Test case 8: Test when `should_use_client_cert` is unavailable and the
267-
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "FALSE".
268-
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
269-
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "FALSE"}):
270-
assert {{ service.client_name }}._use_client_cert_effective() is False
271-
272-
# Test case 9: Test when `should_use_client_cert` is unavailable and the
273-
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not set.
274-
# In this case, the method should return False, which is the default value.
275-
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
276-
with mock.patch.dict(os.environ, clear=True):
277-
assert {{ service.client_name }}._use_client_cert_effective() is False
278-
279-
# Test case 10: Test when `should_use_client_cert` is unavailable and the
280-
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to an invalid value.
281-
# The method should raise a ValueError as the environment variable must be either
282-
# "true" or "false".
283-
if not hasattr(google.auth.transport.mtls, "should_use_client_cert"):
284-
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "unsupported"}):
285-
with pytest.raises(ValueError):
286-
{{ service.client_name }}._use_client_cert_effective()
287-
288-
# Test case 11: Test when `should_use_client_cert` is available and the
289-
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to an invalid value.
290-
# The method should return False as the environment variable is set to an invalid value.
291-
if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
292-
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "unsupported"}):
293-
assert {{ service.client_name }}._use_client_cert_effective() is False
294-
295-
# Test case 12: Test when `should_use_client_cert` is available and the
296-
# `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is unset. Also,
297-
# the GOOGLE_API_CONFIG environment variable is unset.
298-
if hasattr(google.auth.transport.mtls, "should_use_client_cert"):
299-
with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": ""}):
300-
with mock.patch.dict(os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": ""}):
301-
assert {{ service.client_name }}._use_client_cert_effective() is False
302-
303-
def test__get_client_cert_source():
304-
mock_provided_cert_source = mock.Mock()
305-
mock_default_cert_source = mock.Mock()
306-
307-
assert {{ service.client_name }}._get_client_cert_source(None, False) is None
308-
assert {{ service.client_name }}._get_client_cert_source(mock_provided_cert_source, False) is None
309-
assert {{ service.client_name }}._get_client_cert_source(mock_provided_cert_source, True) == mock_provided_cert_source
310-
311-
with mock.patch('google.auth.transport.mtls.has_default_client_cert_source', return_value=True):
312-
with mock.patch('google.auth.transport.mtls.default_client_cert_source', return_value=mock_default_cert_source):
313-
assert {{ service.client_name }}._get_client_cert_source(None, True) is mock_default_cert_source
314-
assert {{ service.client_name }}._get_client_cert_source(mock_provided_cert_source, "true") is mock_provided_cert_source
315175

316176
@mock.patch.object({{ service.client_name }}, "_DEFAULT_ENDPOINT_TEMPLATE", modify_default_endpoint_template({{ service.client_name }}))
317177
{% if 'grpc' in opts.transport %}

packages/gapic-generator/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/client.py

Lines changed: 14 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
from google.api_core import client_options as client_options_lib
2828
from google.api_core import exceptions as core_exceptions
2929
from google.api_core import gapic_v1
30+
from google.api_core.gapic_v1 import client_cert
31+
from google.api_core.gapic_v1 import config_helpers
3032
from google.api_core import retry as retries
3133
from google.auth import credentials as ga_credentials # type: ignore
3234
from google.auth.transport import mtls # type: ignore
@@ -144,30 +146,9 @@ def _get_default_mtls_endpoint(api_endpoint) -> Optional[str]:
144146
_DEFAULT_UNIVERSE = "googleapis.com"
145147

146148
@staticmethod
147-
def _use_client_cert_effective():
148-
"""Returns whether client certificate should be used for mTLS if the
149-
google-auth version supports should_use_client_cert automatic mTLS enablement.
150-
151-
Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var.
152-
153-
Returns:
154-
bool: whether client certificate should be used for mTLS
155-
Raises:
156-
ValueError: (If using a version of google-auth without should_use_client_cert and
157-
GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.)
158-
"""
159-
# check if google-auth version supports should_use_client_cert for automatic mTLS enablement
160-
if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER
161-
return mtls.should_use_client_cert()
162-
else: # pragma: NO COVER
163-
# if unsupported, fallback to reading from env var
164-
use_client_cert_str = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false").lower()
165-
if use_client_cert_str not in ("true", "false"):
166-
raise ValueError(
167-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
168-
" either `true` or `false`"
169-
)
170-
return use_client_cert_str == "true"
149+
def _use_client_cert_effective() -> bool:
150+
"""Returns whether client certificate should be used for mTLS."""
151+
return client_cert.use_client_cert_effective()
171152

172153
@classmethod
173154
def from_service_account_info(cls, info: dict, *args, **kwargs):
@@ -410,44 +391,17 @@ def get_mtls_endpoint_and_cert_source(cls, client_options: Optional[client_optio
410391
return api_endpoint, client_cert_source
411392

412393
@staticmethod
413-
def _read_environment_variables():
414-
"""Returns the environment variables used by the client.
415-
416-
Returns:
417-
Tuple[bool, str, str]: returns the GOOGLE_API_USE_CLIENT_CERTIFICATE,
418-
GOOGLE_API_USE_MTLS_ENDPOINT, and GOOGLE_CLOUD_UNIVERSE_DOMAIN environment variables.
419-
420-
Raises:
421-
ValueError: If GOOGLE_API_USE_CLIENT_CERTIFICATE is not
422-
any of ["true", "false"].
423-
google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT
424-
is not any of ["auto", "never", "always"].
425-
"""
426-
use_client_cert = AssetServiceClient._use_client_cert_effective()
427-
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower()
428-
universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN")
429-
if use_mtls_endpoint not in ("auto", "never", "always"):
430-
raise MutualTLSChannelError("Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`")
431-
return use_client_cert, use_mtls_endpoint, universe_domain_env
394+
def _read_environment_variables() -> Tuple[bool, str, Optional[str]]:
395+
"""Returns the environment variables used by the client."""
396+
return config_helpers.read_environment_variables()
432397

433398
@staticmethod
434-
def _get_client_cert_source(provided_cert_source, use_cert_flag):
435-
"""Return the client cert source to be used by the client.
436-
437-
Args:
438-
provided_cert_source (bytes): The client certificate source provided.
439-
use_cert_flag (bool): A flag indicating whether to use the client certificate.
440-
441-
Returns:
442-
bytes or None: The client cert source to be used by the client.
443-
"""
444-
client_cert_source = None
445-
if use_cert_flag:
446-
if provided_cert_source:
447-
client_cert_source = provided_cert_source
448-
elif mtls.has_default_client_cert_source():
449-
client_cert_source = mtls.default_client_cert_source()
450-
return client_cert_source
399+
def _get_client_cert_source(
400+
provided_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]],
401+
use_cert_flag: bool,
402+
) -> Optional[Callable[[], Tuple[bytes, bytes]]]:
403+
"""Return the client cert source to be used by the client."""
404+
return client_cert.get_client_cert_source(provided_cert_source, use_cert_flag)
451405

452406
@staticmethod
453407
def _get_api_endpoint(api_override, client_cert_source, universe_domain, use_mtls_endpoint) -> str:

0 commit comments

Comments
 (0)