Skip to content

Commit ca20219

Browse files
feat(gapic): support mTLS certificates when available (#1249)
feat: update image to us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209
1 parent 544db1c commit ca20219

File tree

14 files changed

+993
-562
lines changed

14 files changed

+993
-562
lines changed

.librarian/generator-input/setup.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,6 @@
9494
packages=packages,
9595
install_requires=dependencies,
9696
extras_require=extras,
97-
scripts=[
98-
"scripts/fixup_bigtable_v2_keywords.py",
99-
"scripts/fixup_admin_v2_keywords.py",
100-
],
10197
python_requires=">=3.7",
10298
include_package_data=True,
10399
zip_safe=False,

.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-bigtable
44
version: 2.34.0

google/cloud/bigtable_admin_v2/__init__.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,18 @@
1515
#
1616
from google.cloud.bigtable_admin_v2 import gapic_version as package_version
1717

18+
import google.api_core as api_core
19+
import sys
20+
1821
__version__ = package_version.__version__
1922

23+
if sys.version_info >= (3, 8): # pragma: NO COVER
24+
from importlib import metadata
25+
else: # pragma: NO COVER
26+
# TODO(https://github.com/googleapis/python-api-core/issues/835): Remove
27+
# this code path once we drop support for Python 3.7
28+
import importlib_metadata as metadata
29+
2030

2131
from .services.bigtable_instance_admin import BigtableInstanceAdminClient
2232
from .services.bigtable_instance_admin import BigtableInstanceAdminAsyncClient
@@ -143,6 +153,100 @@
143153
from .types.table import RestoreSourceType
144154
from .types.types import Type
145155

156+
if hasattr(api_core, "check_python_version") and hasattr(
157+
api_core, "check_dependency_versions"
158+
): # pragma: NO COVER
159+
api_core.check_python_version("google.cloud.bigtable_admin_v2") # type: ignore
160+
api_core.check_dependency_versions("google.cloud.bigtable_admin_v2") # type: ignore
161+
else: # pragma: NO COVER
162+
# An older version of api_core is installed which does not define the
163+
# functions above. We do equivalent checks manually.
164+
try:
165+
import warnings
166+
import sys
167+
168+
_py_version_str = sys.version.split()[0]
169+
_package_label = "google.cloud.bigtable_admin_v2"
170+
if sys.version_info < (3, 9):
171+
warnings.warn(
172+
"You are using a non-supported Python version "
173+
+ f"({_py_version_str}). Google will not post any further "
174+
+ f"updates to {_package_label} supporting this Python version. "
175+
+ "Please upgrade to the latest Python version, or at "
176+
+ f"least to Python 3.9, and then update {_package_label}.",
177+
FutureWarning,
178+
)
179+
if sys.version_info[:2] == (3, 9):
180+
warnings.warn(
181+
f"You are using a Python version ({_py_version_str}) "
182+
+ f"which Google will stop supporting in {_package_label} in "
183+
+ "January 2026. Please "
184+
+ "upgrade to the latest Python version, or at "
185+
+ "least to Python 3.10, before then, and "
186+
+ f"then update {_package_label}.",
187+
FutureWarning,
188+
)
189+
190+
def parse_version_to_tuple(version_string: str):
191+
"""Safely converts a semantic version string to a comparable tuple of integers.
192+
Example: "4.25.8" -> (4, 25, 8)
193+
Ignores non-numeric parts and handles common version formats.
194+
Args:
195+
version_string: Version string in the format "x.y.z" or "x.y.z<suffix>"
196+
Returns:
197+
Tuple of integers for the parsed version string.
198+
"""
199+
parts = []
200+
for part in version_string.split("."):
201+
try:
202+
parts.append(int(part))
203+
except ValueError:
204+
# If it's a non-numeric part (e.g., '1.0.0b1' -> 'b1'), stop here.
205+
# This is a simplification compared to 'packaging.parse_version', but sufficient
206+
# for comparing strictly numeric semantic versions.
207+
break
208+
return tuple(parts)
209+
210+
def _get_version(dependency_name):
211+
try:
212+
version_string: str = metadata.version(dependency_name)
213+
parsed_version = parse_version_to_tuple(version_string)
214+
return (parsed_version, version_string)
215+
except Exception:
216+
# Catch exceptions from metadata.version() (e.g., PackageNotFoundError)
217+
# or errors during parse_version_to_tuple
218+
return (None, "--")
219+
220+
_dependency_package = "google.protobuf"
221+
_next_supported_version = "4.25.8"
222+
_next_supported_version_tuple = (4, 25, 8)
223+
_recommendation = " (we recommend 6.x)"
224+
(_version_used, _version_used_string) = _get_version(_dependency_package)
225+
if _version_used and _version_used < _next_supported_version_tuple:
226+
warnings.warn(
227+
f"Package {_package_label} depends on "
228+
+ f"{_dependency_package}, currently installed at version "
229+
+ f"{_version_used_string}. Future updates to "
230+
+ f"{_package_label} will require {_dependency_package} at "
231+
+ f"version {_next_supported_version} or higher{_recommendation}."
232+
+ " Please ensure "
233+
+ "that either (a) your Python environment doesn't pin the "
234+
+ f"version of {_dependency_package}, so that updates to "
235+
+ f"{_package_label} can require the higher version, or "
236+
+ "(b) you manually update your Python environment to use at "
237+
+ f"least version {_next_supported_version} of "
238+
+ f"{_dependency_package}.",
239+
FutureWarning,
240+
)
241+
except Exception:
242+
warnings.warn(
243+
"Could not determine the version of Python "
244+
+ "currently being used. To continue receiving "
245+
+ "updates for {_package_label}, ensure you are "
246+
+ "using a supported version of Python; see "
247+
+ "https://devguide.python.org/versions/"
248+
)
249+
146250
__all__ = (
147251
"BaseBigtableTableAdminAsyncClient",
148252
"BigtableInstanceAdminAsyncClient",

google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/client.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,34 @@ def _get_default_mtls_endpoint(api_endpoint):
161161
_DEFAULT_ENDPOINT_TEMPLATE = "bigtableadmin.{UNIVERSE_DOMAIN}"
162162
_DEFAULT_UNIVERSE = "googleapis.com"
163163

164+
@staticmethod
165+
def _use_client_cert_effective():
166+
"""Returns whether client certificate should be used for mTLS if the
167+
google-auth version supports should_use_client_cert automatic mTLS enablement.
168+
169+
Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var.
170+
171+
Returns:
172+
bool: whether client certificate should be used for mTLS
173+
Raises:
174+
ValueError: (If using a version of google-auth without should_use_client_cert and
175+
GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.)
176+
"""
177+
# check if google-auth version supports should_use_client_cert for automatic mTLS enablement
178+
if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER
179+
return mtls.should_use_client_cert()
180+
else: # pragma: NO COVER
181+
# if unsupported, fallback to reading from env var
182+
use_client_cert_str = os.getenv(
183+
"GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
184+
).lower()
185+
if use_client_cert_str not in ("true", "false"):
186+
raise ValueError(
187+
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
188+
" either `true` or `false`"
189+
)
190+
return use_client_cert_str == "true"
191+
164192
@classmethod
165193
def from_service_account_info(cls, info: dict, *args, **kwargs):
166194
"""Creates an instance of this client using the provided credentials
@@ -503,20 +531,16 @@ def get_mtls_endpoint_and_cert_source(
503531
)
504532
if client_options is None:
505533
client_options = client_options_lib.ClientOptions()
506-
use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
534+
use_client_cert = BigtableInstanceAdminClient._use_client_cert_effective()
507535
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
508-
if use_client_cert not in ("true", "false"):
509-
raise ValueError(
510-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
511-
)
512536
if use_mtls_endpoint not in ("auto", "never", "always"):
513537
raise MutualTLSChannelError(
514538
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
515539
)
516540

517541
# Figure out the client cert source to use.
518542
client_cert_source = None
519-
if use_client_cert == "true":
543+
if use_client_cert:
520544
if client_options.client_cert_source:
521545
client_cert_source = client_options.client_cert_source
522546
elif mtls.has_default_client_cert_source():
@@ -548,20 +572,14 @@ def _read_environment_variables():
548572
google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT
549573
is not any of ["auto", "never", "always"].
550574
"""
551-
use_client_cert = os.getenv(
552-
"GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
553-
).lower()
575+
use_client_cert = BigtableInstanceAdminClient._use_client_cert_effective()
554576
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower()
555577
universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN")
556-
if use_client_cert not in ("true", "false"):
557-
raise ValueError(
558-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
559-
)
560578
if use_mtls_endpoint not in ("auto", "never", "always"):
561579
raise MutualTLSChannelError(
562580
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
563581
)
564-
return use_client_cert == "true", use_mtls_endpoint, universe_domain_env
582+
return use_client_cert, use_mtls_endpoint, universe_domain_env
565583

566584
@staticmethod
567585
def _get_client_cert_source(provided_cert_source, use_cert_flag):

google/cloud/bigtable_admin_v2/services/bigtable_table_admin/client.py

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,34 @@ def _get_default_mtls_endpoint(api_endpoint):
162162
_DEFAULT_ENDPOINT_TEMPLATE = "bigtableadmin.{UNIVERSE_DOMAIN}"
163163
_DEFAULT_UNIVERSE = "googleapis.com"
164164

165+
@staticmethod
166+
def _use_client_cert_effective():
167+
"""Returns whether client certificate should be used for mTLS if the
168+
google-auth version supports should_use_client_cert automatic mTLS enablement.
169+
170+
Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var.
171+
172+
Returns:
173+
bool: whether client certificate should be used for mTLS
174+
Raises:
175+
ValueError: (If using a version of google-auth without should_use_client_cert and
176+
GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.)
177+
"""
178+
# check if google-auth version supports should_use_client_cert for automatic mTLS enablement
179+
if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER
180+
return mtls.should_use_client_cert()
181+
else: # pragma: NO COVER
182+
# if unsupported, fallback to reading from env var
183+
use_client_cert_str = os.getenv(
184+
"GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
185+
).lower()
186+
if use_client_cert_str not in ("true", "false"):
187+
raise ValueError(
188+
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be"
189+
" either `true` or `false`"
190+
)
191+
return use_client_cert_str == "true"
192+
165193
@classmethod
166194
def from_service_account_info(cls, info: dict, *args, **kwargs):
167195
"""Creates an instance of this client using the provided credentials
@@ -510,20 +538,16 @@ def get_mtls_endpoint_and_cert_source(
510538
)
511539
if client_options is None:
512540
client_options = client_options_lib.ClientOptions()
513-
use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
541+
use_client_cert = BaseBigtableTableAdminClient._use_client_cert_effective()
514542
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
515-
if use_client_cert not in ("true", "false"):
516-
raise ValueError(
517-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
518-
)
519543
if use_mtls_endpoint not in ("auto", "never", "always"):
520544
raise MutualTLSChannelError(
521545
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
522546
)
523547

524548
# Figure out the client cert source to use.
525549
client_cert_source = None
526-
if use_client_cert == "true":
550+
if use_client_cert:
527551
if client_options.client_cert_source:
528552
client_cert_source = client_options.client_cert_source
529553
elif mtls.has_default_client_cert_source():
@@ -555,20 +579,14 @@ def _read_environment_variables():
555579
google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT
556580
is not any of ["auto", "never", "always"].
557581
"""
558-
use_client_cert = os.getenv(
559-
"GOOGLE_API_USE_CLIENT_CERTIFICATE", "false"
560-
).lower()
582+
use_client_cert = BaseBigtableTableAdminClient._use_client_cert_effective()
561583
use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower()
562584
universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN")
563-
if use_client_cert not in ("true", "false"):
564-
raise ValueError(
565-
"Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
566-
)
567585
if use_mtls_endpoint not in ("auto", "never", "always"):
568586
raise MutualTLSChannelError(
569587
"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
570588
)
571-
return use_client_cert == "true", use_mtls_endpoint, universe_domain_env
589+
return use_client_cert, use_mtls_endpoint, universe_domain_env
572590

573591
@staticmethod
574592
def _get_client_cert_source(provided_cert_source, use_cert_flag):

0 commit comments

Comments
 (0)