Skip to content

Commit 5bd2eed

Browse files
authored
feat(gapic): support mTLS certificates when available (#1071)
chore: librarian update image pull request 20251216T194655Z
1 parent 1415883 commit 5bd2eed

File tree

11 files changed

+800
-138
lines changed

11 files changed

+800
-138
lines changed

.librarian/generator-input/noxfile.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,15 @@
3232
ISORT_VERSION = "isort==5.11.0"
3333
LINT_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"]
3434

35-
DEFAULT_PYTHON_VERSION = "3.10"
35+
DEFAULT_PYTHON_VERSION = "3.14"
3636

3737
UNIT_TEST_PYTHON_VERSIONS: List[str] = [
3838
"3.9",
3939
"3.10",
4040
"3.11",
4141
"3.12",
4242
"3.13",
43+
"3.14",
4344
]
4445
UNIT_TEST_STANDARD_DEPENDENCIES = [
4546
"mock",
@@ -58,7 +59,7 @@
5859
UNIT_TEST_EXTRAS: List[str] = []
5960
UNIT_TEST_EXTRAS_BY_PYTHON: Dict[str, List[str]] = {}
6061

61-
SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.12"]
62+
SYSTEM_TEST_PYTHON_VERSIONS: List[str] = [DEFAULT_PYTHON_VERSION]
6263
SYSTEM_TEST_STANDARD_DEPENDENCIES: List[str] = [
6364
"mock",
6465
"pytest",
@@ -79,7 +80,12 @@
7980
CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute()
8081

8182
nox.options.sessions = [
82-
"unit",
83+
"unit-3.9",
84+
"unit-3.10",
85+
"unit-3.11",
86+
"unit-3.12",
87+
"unit-3.13",
88+
"unit-3.14",
8389
"system",
8490
"cover",
8591
"lint",
@@ -143,7 +149,7 @@ def format(session):
143149
@nox.session(python=DEFAULT_PYTHON_VERSION)
144150
def lint_setup_py(session):
145151
"""Verify that setup.py is valid (including RST check)."""
146-
session.install("docutils", "pygments")
152+
session.install("setuptools", "docutils", "pygments")
147153
session.run("python", "setup.py", "check", "--restructuredtext", "--strict")
148154

149155

@@ -182,8 +188,8 @@ def install_unittest_dependencies(session, *constraints):
182188
)
183189
def unit(session, protobuf_implementation):
184190
# Install all test dependencies, then install this package in-place.
185-
186-
if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12", "3.13"):
191+
py_version = tuple([int(v) for v in session.python.split(".")])
192+
if protobuf_implementation == "cpp" and py_version >= (3, 11):
187193
session.skip("cpp implementation is not supported in python 3.11+")
188194

189195
constraints_path = str(
@@ -383,15 +389,16 @@ def docfx(session):
383389
)
384390

385391

386-
@nox.session(python="3.13")
392+
@nox.session(python=DEFAULT_PYTHON_VERSION)
387393
@nox.parametrize(
388394
"protobuf_implementation",
389395
["python", "upb", "cpp"],
390396
)
391397
def prerelease_deps(session, protobuf_implementation):
392398
"""Run all tests with prerelease versions of dependencies installed."""
393399

394-
if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12", "3.13"):
400+
py_version = tuple([int(v) for v in session.python.split(".")])
401+
if protobuf_implementation == "cpp" and py_version >= (3, 11):
395402
session.skip("cpp implementation is not supported in python 3.11+")
396403

397404
# Install all dependencies

.librarian/generator-input/setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@
8585
"Programming Language :: Python :: 3.10",
8686
"Programming Language :: Python :: 3.11",
8787
"Programming Language :: Python :: 3.12",
88+
"Programming Language :: Python :: 3.13",
89+
"Programming Language :: Python :: 3.14",
8890
"Operating System :: OS Independent",
8991
"Topic :: Internet",
9092
],

.librarian/state.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:8e2c32496077054105bd06c54a59d6a6694287bc053588e24debe6da6920ad91
1+
image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209
22
libraries:
33
- id: google-cloud-logging
44
version: 3.13.0

google/cloud/logging_v2/services/config_service_v2/client.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,34 @@ def _get_default_mtls_endpoint(api_endpoint):
153153
_DEFAULT_ENDPOINT_TEMPLATE = "logging.{UNIVERSE_DOMAIN}"
154154
_DEFAULT_UNIVERSE = "googleapis.com"
155155

156+
@staticmethod
157+
def _use_client_cert_effective():
158+
"""Returns whether client certificate should be used for mTLS if the
159+
google-auth version supports should_use_client_cert automatic mTLS enablement.
160+
161+
Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var.
162+
163+
Returns:
164+
bool: whether client certificate should be used for mTLS
165+
Raises:
166+
ValueError: (If using a version of google-auth without should_use_client_cert and
167+
GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.)
168+
"""
169+
# check if google-auth version supports should_use_client_cert for automatic mTLS enablement
170+
if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER
171+
return mtls.should_use_client_cert()
172+
else: # pragma: NO COVER
173+
# if unsupported, fallback to reading from env var
174+
use_client_cert_str = os.getenv(
175+
"GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
176+
).lower()
177+
if use_client_cert_str not in ("true", "false"):
178+
raise ValueError(
179+
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
180+
" either `true` or `false`"
181+
)
182+
return use_client_cert_str == "true"
183+
156184
@classmethod
157185
def from_service_account_info(cls, info: dict, *args, **kwargs):
158186
"""Creates an instance of this client using the provided credentials
@@ -452,20 +480,16 @@ def get_mtls_endpoint_and_cert_source(
452480
)
453481
if client_options is None:
454482
client_options = client_options_lib.ClientOptions()
455-
use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
483+
use_client_cert = ConfigServiceV2Client._use_client_cert_effective()
456484
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
457-
if use_client_cert not in ("true", "false"):
458-
raise ValueError(
459-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
460-
)
461485
if use_mtls_endpoint not in ("auto", "never", "always"):
462486
raise MutualTLSChannelError(
463487
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
464488
)
465489

466490
# Figure out the client cert source to use.
467491
client_cert_source = None
468-
if use_client_cert == "true":
492+
if use_client_cert:
469493
if client_options.client_cert_source:
470494
client_cert_source = client_options.client_cert_source
471495
elif mtls.has_default_client_cert_source():
@@ -497,20 +521,14 @@ def _read_environment_variables():
497521
google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT
498522
is not any of ["auto", "never", "always"].
499523
"""
500-
use_client_cert = os.getenv(
501-
"GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
502-
).lower()
524+
use_client_cert = ConfigServiceV2Client._use_client_cert_effective()
503525
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower()
504526
universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN")
505-
if use_client_cert not in ("true", "false"):
506-
raise ValueError(
507-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
508-
)
509527
if use_mtls_endpoint not in ("auto", "never", "always"):
510528
raise MutualTLSChannelError(
511529
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
512530
)
513-
return use_client_cert == "true", use_mtls_endpoint, universe_domain_env
531+
return use_client_cert, use_mtls_endpoint, universe_domain_env
514532

515533
@staticmethod
516534
def _get_client_cert_source(provided_cert_source, use_cert_flag):

google/cloud/logging_v2/services/logging_service_v2/client.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,34 @@ def _get_default_mtls_endpoint(api_endpoint):
152152
_DEFAULT_ENDPOINT_TEMPLATE = "logging.{UNIVERSE_DOMAIN}"
153153
_DEFAULT_UNIVERSE = "googleapis.com"
154154

155+
@staticmethod
156+
def _use_client_cert_effective():
157+
"""Returns whether client certificate should be used for mTLS if the
158+
google-auth version supports should_use_client_cert automatic mTLS enablement.
159+
160+
Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var.
161+
162+
Returns:
163+
bool: whether client certificate should be used for mTLS
164+
Raises:
165+
ValueError: (If using a version of google-auth without should_use_client_cert and
166+
GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.)
167+
"""
168+
# check if google-auth version supports should_use_client_cert for automatic mTLS enablement
169+
if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER
170+
return mtls.should_use_client_cert()
171+
else: # pragma: NO COVER
172+
# if unsupported, fallback to reading from env var
173+
use_client_cert_str = os.getenv(
174+
"GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
175+
).lower()
176+
if use_client_cert_str not in ("true", "false"):
177+
raise ValueError(
178+
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
179+
" either `true` or `false`"
180+
)
181+
return use_client_cert_str == "true"
182+
155183
@classmethod
156184
def from_service_account_info(cls, info: dict, *args, **kwargs):
157185
"""Creates an instance of this client using the provided credentials
@@ -334,20 +362,16 @@ def get_mtls_endpoint_and_cert_source(
334362
)
335363
if client_options is None:
336364
client_options = client_options_lib.ClientOptions()
337-
use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
365+
use_client_cert = LoggingServiceV2Client._use_client_cert_effective()
338366
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
339-
if use_client_cert not in ("true", "false"):
340-
raise ValueError(
341-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
342-
)
343367
if use_mtls_endpoint not in ("auto", "never", "always"):
344368
raise MutualTLSChannelError(
345369
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
346370
)
347371

348372
# Figure out the client cert source to use.
349373
client_cert_source = None
350-
if use_client_cert == "true":
374+
if use_client_cert:
351375
if client_options.client_cert_source:
352376
client_cert_source = client_options.client_cert_source
353377
elif mtls.has_default_client_cert_source():
@@ -379,20 +403,14 @@ def _read_environment_variables():
379403
google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT
380404
is not any of ["auto", "never", "always"].
381405
"""
382-
use_client_cert = os.getenv(
383-
"GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
384-
).lower()
406+
use_client_cert = LoggingServiceV2Client._use_client_cert_effective()
385407
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower()
386408
universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN")
387-
if use_client_cert not in ("true", "false"):
388-
raise ValueError(
389-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
390-
)
391409
if use_mtls_endpoint not in ("auto", "never", "always"):
392410
raise MutualTLSChannelError(
393411
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
394412
)
395-
return use_client_cert == "true", use_mtls_endpoint, universe_domain_env
413+
return use_client_cert, use_mtls_endpoint, universe_domain_env
396414

397415
@staticmethod
398416
def _get_client_cert_source(provided_cert_source, use_cert_flag):

google/cloud/logging_v2/services/metrics_service_v2/client.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,34 @@ def _get_default_mtls_endpoint(api_endpoint):
151151
_DEFAULT_ENDPOINT_TEMPLATE = "logging.{UNIVERSE_DOMAIN}"
152152
_DEFAULT_UNIVERSE = "googleapis.com"
153153

154+
@staticmethod
155+
def _use_client_cert_effective():
156+
"""Returns whether client certificate should be used for mTLS if the
157+
google-auth version supports should_use_client_cert automatic mTLS enablement.
158+
159+
Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var.
160+
161+
Returns:
162+
bool: whether client certificate should be used for mTLS
163+
Raises:
164+
ValueError: (If using a version of google-auth without should_use_client_cert and
165+
GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.)
166+
"""
167+
# check if google-auth version supports should_use_client_cert for automatic mTLS enablement
168+
if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER
169+
return mtls.should_use_client_cert()
170+
else: # pragma: NO COVER
171+
# if unsupported, fallback to reading from env var
172+
use_client_cert_str = os.getenv(
173+
"GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
174+
).lower()
175+
if use_client_cert_str not in ("true", "false"):
176+
raise ValueError(
177+
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
178+
" either `true` or `false`"
179+
)
180+
return use_client_cert_str == "true"
181+
154182
@classmethod
155183
def from_service_account_info(cls, info: dict, *args, **kwargs):
156184
"""Creates an instance of this client using the provided credentials
@@ -333,20 +361,16 @@ def get_mtls_endpoint_and_cert_source(
333361
)
334362
if client_options is None:
335363
client_options = client_options_lib.ClientOptions()
336-
use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
364+
use_client_cert = MetricsServiceV2Client._use_client_cert_effective()
337365
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
338-
if use_client_cert not in ("true", "false"):
339-
raise ValueError(
340-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
341-
)
342366
if use_mtls_endpoint not in ("auto", "never", "always"):
343367
raise MutualTLSChannelError(
344368
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
345369
)
346370

347371
# Figure out the client cert source to use.
348372
client_cert_source = None
349-
if use_client_cert == "true":
373+
if use_client_cert:
350374
if client_options.client_cert_source:
351375
client_cert_source = client_options.client_cert_source
352376
elif mtls.has_default_client_cert_source():
@@ -378,20 +402,14 @@ def _read_environment_variables():
378402
google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT
379403
is not any of ["auto", "never", "always"].
380404
"""
381-
use_client_cert = os.getenv(
382-
"GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
383-
).lower()
405+
use_client_cert = MetricsServiceV2Client._use_client_cert_effective()
384406
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower()
385407
universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN")
386-
if use_client_cert not in ("true", "false"):
387-
raise ValueError(
388-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
389-
)
390408
if use_mtls_endpoint not in ("auto", "never", "always"):
391409
raise MutualTLSChannelError(
392410
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
393411
)
394-
return use_client_cert == "true", use_mtls_endpoint, universe_domain_env
412+
return use_client_cert, use_mtls_endpoint, universe_domain_env
395413

396414
@staticmethod
397415
def _get_client_cert_source(provided_cert_source, use_cert_flag):

noxfile.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
1616

17+
# DO NOT EDIT THIS FILE OUTSIDE OF `.librarian/generator-input`
18+
# The source of truth for this file is `.librarian/generator-input`
19+
20+
1721
# Generated by synthtool. DO NOT EDIT!
1822

1923
from __future__ import absolute_import

setup.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515
#
16+
17+
# DO NOT EDIT THIS FILE OUTSIDE OF `.librarian/generator-input`
18+
# The source of truth for this file is `.librarian/generator-input`
19+
1620
import io
1721
import os
1822

0 commit comments

Comments
 (0)