diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 50e6d044e..869743b2d 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -46,16 +46,16 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@babb554ede22fd5605947329c4d04d8e7a0b8155 # v3.27.7 + uses: github/codeql-action/init@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1 with: languages: ${{ matrix.language }} # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). # If this step fails, then you should remove it and run the build manually - name: Autobuild - uses: github/codeql-action/autobuild@babb554ede22fd5605947329c4d04d8e7a0b8155 # v3.27.7 + uses: github/codeql-action/autobuild@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@babb554ede22fd5605947329c4d04d8e7a0b8155 # v3.27.7 + uses: github/codeql-action/analyze@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 653a2d6fc..fd858ae79 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -57,7 +57,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: SARIF file path: results.sarif @@ -65,6 +65,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@babb554ede22fd5605947329c4d04d8e7a0b8155 # v3.27.7 + uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1 with: sarif_file: resultsFiltered.sarif diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b4af63dd9..dceb894b2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -81,6 +81,8 @@ jobs: POSTGRES_DB:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_DB POSTGRES_CAS_CONNECTION_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CAS_CONNECTION_NAME POSTGRES_CAS_PASS:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CAS_PASS + POSTGRES_CUSTOMER_CAS_CONNECTION_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CUSTOMER_CAS_CONNECTION_NAME + POSTGRES_CUSTOMER_CAS_PASS:${{ vars.GOOGLE_CLOUD_PROJECT }}/POSTGRES_CUSTOMER_CAS_PASS SQLSERVER_CONNECTION_NAME:${{ vars.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_CONNECTION_NAME SQLSERVER_USER:${{ vars.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_USER SQLSERVER_PASS:${{ vars.GOOGLE_CLOUD_PROJECT }}/SQLSERVER_PASS @@ -102,6 +104,8 @@ jobs: POSTGRES_DB: "${{ steps.secrets.outputs.POSTGRES_DB }}" POSTGRES_CAS_CONNECTION_NAME: "${{ steps.secrets.outputs.POSTGRES_CAS_CONNECTION_NAME }}" POSTGRES_CAS_PASS: "${{ steps.secrets.outputs.POSTGRES_CAS_PASS }}" + POSTGRES_CUSTOMER_CAS_CONNECTION_NAME: "${{ steps.secrets.outputs.POSTGRES_CUSTOMER_CAS_CONNECTION_NAME }}" + POSTGRES_CUSTOMER_CAS_PASS: "${{ steps.secrets.outputs.POSTGRES_CUSTOMER_CAS_PASS }}" SQLSERVER_CONNECTION_NAME: "${{ steps.secrets.outputs.SQLSERVER_CONNECTION_NAME }}" SQLSERVER_USER: "${{ steps.secrets.outputs.SQLSERVER_USER }}" SQLSERVER_PASS: "${{ steps.secrets.outputs.SQLSERVER_PASS }}" diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index 69d7444fc..0f4453e1c 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -149,17 +149,17 @@ cryptography==42.0.4 \ # via # gcp-releasetool # secretstorage -distlib==0.3.6 \ - --hash=sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46 \ - --hash=sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e +distlib==0.3.9 \ + --hash=sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87 \ + --hash=sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403 # via virtualenv docutils==0.19 \ --hash=sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6 \ --hash=sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc # via readme-renderer -filelock==3.8.0 \ - --hash=sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc \ - --hash=sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4 +filelock==3.16.1 \ + --hash=sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0 \ + --hash=sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435 # via virtualenv gcp-docuploader==0.6.4 \ --hash=sha256:01486419e24633af78fd0167db74a2763974765ee8078ca6eb6964d0ebd388af \ @@ -290,9 +290,9 @@ jeepney==0.8.0 \ # via # keyring # secretstorage -jinja2==3.1.4 \ - --hash=sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369 \ - --hash=sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d +jinja2==3.1.5 \ + --hash=sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb \ + --hash=sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb # via gcp-releasetool keyring==23.11.0 \ --hash=sha256:3dd30011d555f1345dec2c262f0153f2f0ca6bca041fb1dc4588349bb4c0ac1e \ @@ -360,9 +360,9 @@ pkginfo==1.8.3 \ --hash=sha256:848865108ec99d4901b2f7e84058b6e7660aae8ae10164e015a6dcf5b242a594 \ --hash=sha256:a84da4318dd86f870a9447a8c98340aa06216bfc6f2b7bdc4b8766984ae1867c # via twine -platformdirs==2.5.4 \ - --hash=sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7 \ - --hash=sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10 +platformdirs==4.3.6 \ + --hash=sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907 \ + --hash=sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb # via virtualenv protobuf==3.20.3 \ --hash=sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7 \ @@ -482,9 +482,9 @@ urllib3==1.26.19 \ # via # requests # twine -virtualenv==20.16.7 \ - --hash=sha256:8691e3ff9387f743e00f6bb20f70121f5e4f596cae754531f2b3b3a1b1ac696e \ - --hash=sha256:efd66b00386fdb7dbe4822d172303f40cd05e50e01740b19ea42425cbe653e29 +virtualenv==20.26.6 \ + --hash=sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48 \ + --hash=sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2 # via nox webencodings==0.5.1 \ --hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \ diff --git a/CHANGELOG.md b/CHANGELOG.md index 1467f5436..9d663c9e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.16.0](https://github.com/GoogleCloudPlatform/cloud-sql-python-connector/compare/v1.15.0...v1.16.0) (2025-01-13) + + +### Features + +* add support for `GOOGLE_CLOUD_UNIVERSE_DOMAIN` env var ([#1221](https://github.com/GoogleCloudPlatform/cloud-sql-python-connector/issues/1221)) ([ac77932](https://github.com/GoogleCloudPlatform/cloud-sql-python-connector/commit/ac77932e7f6c592c73abc75a153b61571ba6c7ff)) + ## [1.15.0](https://github.com/GoogleCloudPlatform/cloud-sql-python-connector/compare/v1.14.0...v1.15.0) (2024-12-10) diff --git a/google/cloud/sql/connector/connector.py b/google/cloud/sql/connector/connector.py index 7c5fb20de..5160513fd 100755 --- a/google/cloud/sql/connector/connector.py +++ b/google/cloud/sql/connector/connector.py @@ -19,9 +19,10 @@ import asyncio from functools import partial import logging +import os from threading import Thread from types import TracebackType -from typing import Any, Optional, Type, Union +from typing import Any, Optional, Union import google.auth from google.auth.credentials import Credentials @@ -65,7 +66,7 @@ def __init__( user_agent: Optional[str] = None, universe_domain: Optional[str] = None, refresh_strategy: str | RefreshStrategy = RefreshStrategy.BACKGROUND, - resolver: Type[DefaultResolver] | Type[DnsResolver] = DefaultResolver, + resolver: type[DefaultResolver] | type[DnsResolver] = DefaultResolver, ) -> None: """Initializes a Connector instance. @@ -171,7 +172,11 @@ def __init__( if isinstance(ip_type, str): ip_type = IPTypes._from_str(ip_type) self._ip_type = ip_type - self._universe_domain = universe_domain + # check for universe domain arg and then env var + if universe_domain: + self._universe_domain = universe_domain + else: + self._universe_domain = os.environ.get("GOOGLE_CLOUD_UNIVERSE_DOMAIN") # type: ignore # construct service endpoint for Cloud SQL Admin API calls if not sqladmin_api_endpoint: self._sqladmin_api_endpoint = ( @@ -391,7 +396,7 @@ def __enter__(self) -> Any: def __exit__( self, - exc_type: Optional[Type[BaseException]], + exc_type: Optional[type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: @@ -404,7 +409,7 @@ async def __aenter__(self) -> Any: async def __aexit__( self, - exc_type: Optional[Type[BaseException]], + exc_type: Optional[type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: diff --git a/google/cloud/sql/connector/enums.py b/google/cloud/sql/connector/enums.py index 86556ed2b..e6b56af0e 100644 --- a/google/cloud/sql/connector/enums.py +++ b/google/cloud/sql/connector/enums.py @@ -19,9 +19,10 @@ from google.cloud.sql.connector.exceptions import IncompatibleDriverError +# TODO: Replace Enum with StrEnum when Python 3.11 is minimum supported version class RefreshStrategy(Enum): - LAZY: str = "LAZY" - BACKGROUND: str = "BACKGROUND" + LAZY = "LAZY" + BACKGROUND = "BACKGROUND" @classmethod def _missing_(cls, value: object) -> None: @@ -37,9 +38,9 @@ def _from_str(cls, refresh_strategy: str) -> RefreshStrategy: class IPTypes(Enum): - PUBLIC: str = "PRIMARY" - PRIVATE: str = "PRIVATE" - PSC: str = "PSC" + PUBLIC = "PRIMARY" + PRIVATE = "PRIVATE" + PSC = "PSC" @classmethod def _missing_(cls, value: object) -> None: diff --git a/google/cloud/sql/connector/version.py b/google/cloud/sql/connector/version.py index 587a7adbf..f42f3f3f9 100644 --- a/google/cloud/sql/connector/version.py +++ b/google/cloud/sql/connector/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "1.15.0" +__version__ = "1.16.0" diff --git a/requirements-test.txt b/requirements-test.txt index 40102fc21..4056cc3d5 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,8 +1,8 @@ pytest==8.3.4 mock==5.1.0 pytest-cov==6.0.0 -pytest-asyncio==0.24.0 -SQLAlchemy[asyncio]==2.0.36 +pytest-asyncio==0.25.2 +SQLAlchemy[asyncio]==2.0.37 sqlalchemy-pytds==1.0.2 sqlalchemy-stubs==0.4 PyMySQL==1.1.1 diff --git a/requirements.txt b/requirements.txt index 75b53b946..d6eacff3b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ aiofiles==24.1.0 -aiohttp==3.11.10 +aiohttp==3.11.11 cryptography==44.0.0 dnspython==2.7.0 Requests==2.32.3 -google-auth==2.36.0 +google-auth==2.37.0 diff --git a/tests/system/test_pg8000_connection.py b/tests/system/test_pg8000_connection.py index ea5b2ee69..b56a8e823 100644 --- a/tests/system/test_pg8000_connection.py +++ b/tests/system/test_pg8000_connection.py @@ -137,3 +137,19 @@ def test_CAS_pg8000_connection() -> None: curr_time = time[0] assert type(curr_time) is datetime connector.close() + + +def test_customer_managed_CAS_pg8000_connection() -> None: + """Basic test to get time from database.""" + inst_conn_name = os.environ["POSTGRES_CUSTOMER_CAS_CONNECTION_NAME"] + user = os.environ["POSTGRES_USER"] + password = os.environ["POSTGRES_CUSTOMER_CAS_PASS"] + db = os.environ["POSTGRES_DB"] + + engine, connector = create_sqlalchemy_engine(inst_conn_name, user, password, db) + with engine.connect() as conn: + time = conn.execute(sqlalchemy.text("SELECT NOW()")).fetchone() + conn.commit() + curr_time = time[0] + assert type(curr_time) is datetime + connector.close() diff --git a/tests/unit/test_connector.py b/tests/unit/test_connector.py index d4f53ed51..eaf188c3e 100644 --- a/tests/unit/test_connector.py +++ b/tests/unit/test_connector.py @@ -15,6 +15,7 @@ """ import asyncio +import os from typing import Union from aiohttp import ClientResponseError @@ -428,3 +429,25 @@ def test_configured_universe_domain_mismatched_credentials( "is the default." ) assert exc_info.value.args[0] == err_msg + + +def test_configured_universe_domain_env_var( + fake_credentials: Credentials, +) -> None: + """Test that configured universe domain succeeds with universe + domain set via GOOGLE_CLOUD_UNIVERSE_DOMAIN env var. + """ + universe_domain = "test-universe.test" + # set fake credentials to be configured for the universe domain + fake_credentials._universe_domain = universe_domain + # set environment variable + os.environ["GOOGLE_CLOUD_UNIVERSE_DOMAIN"] = universe_domain + # Note: we are not passing universe_domain arg, env var should set it + with Connector(credentials=fake_credentials) as connector: + # test universe domain was configured + assert connector._universe_domain == universe_domain + # test property and service endpoint construction + assert connector.universe_domain == universe_domain + assert connector._sqladmin_api_endpoint == f"https://sqladmin.{universe_domain}" + # unset env var + del os.environ["GOOGLE_CLOUD_UNIVERSE_DOMAIN"]