Skip to content

Commit d804b8f

Browse files
committed
feat: Support Developer Edition connections
This PR adds support for Developer Edition connections via the SqlDataService. It includes a fallback mechanism to standard IP connections if the SqlDataService is not supported by the instance edition. See GoogleCloudPlatform/cloud-sql-go-connector#1108
1 parent bcc1cbf commit d804b8f

16 files changed

Lines changed: 1692 additions & 157 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ dist/
1010
sponge_log.xml
1111
.envrc
1212
*.iml
13+
build/
1314
.mypy_cache/
1415
.nox/
1516
.pytest_cache/

google/cloud/sql/connector/asyncpg.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
limitations under the License.
1515
"""
1616

17+
from __future__ import annotations
18+
1719
import ssl
1820
from typing import Any, TYPE_CHECKING
1921

@@ -24,16 +26,15 @@
2426

2527

2628
async def connect(
27-
ip_address: str, ctx: ssl.SSLContext, **kwargs: Any
28-
) -> "asyncpg.Connection":
29+
ip_address: str, ctx: ssl.SSLContext | None, **kwargs: Any
30+
) -> asyncpg.Connection:
2931
"""Helper function to create an asyncpg DB-API connection object.
3032
3133
Args:
3234
ip_address (str): A string containing an IP address for the Cloud SQL
3335
instance.
3436
ctx (ssl.SSLContext): An SSLContext object created from the Cloud SQL
35-
server CA cert and ephemeral cert.
36-
server CA cert and ephemeral cert.
37+
server CA cert and ephemeral cert. Pass None to disable SSL.
3738
kwargs: Keyword arguments for establishing asyncpg connection
3839
object to Cloud SQL instance.
3940
@@ -53,14 +54,18 @@ async def connect(
5354
user = kwargs.pop("user")
5455
db = kwargs.pop("db")
5556
passwd = kwargs.pop("password", None)
57+
port = kwargs.pop("port", SERVER_PROXY_PORT)
5658

57-
return await asyncpg.connect(
58-
user=user,
59-
database=db,
60-
password=passwd,
61-
host=ip_address,
62-
port=SERVER_PROXY_PORT,
63-
ssl=ctx,
64-
direct_tls=True,
59+
connect_args = {
60+
"user": user,
61+
"database": db,
62+
"password": passwd,
63+
"host": ip_address,
64+
"port": port,
6565
**kwargs,
66-
)
66+
}
67+
if ctx is not None:
68+
connect_args["ssl"] = ctx
69+
connect_args["direct_tls"] = True
70+
71+
return await asyncpg.connect(**connect_args)

google/cloud/sql/connector/client.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,13 @@ async def _get_metadata(
173173
if psc_dns_names:
174174
ip_addresses["PSC"] = psc_dns_names
175175

176+
server_ca_cert = None
177+
if "serverCaCert" in ret_dict and "cert" in ret_dict["serverCaCert"]:
178+
server_ca_cert = ret_dict["serverCaCert"]["cert"]
179+
176180
return {
177181
"ip_addresses": ip_addresses,
178-
"server_ca_cert": ret_dict["serverCaCert"]["cert"],
182+
"server_ca_cert": server_ca_cert,
179183
"database_version": ret_dict["databaseVersion"],
180184
}
181185

@@ -271,7 +275,11 @@ async def _get_ephemeral(
271275
finally:
272276
resp.raise_for_status()
273277

274-
ephemeral_cert: str = ret_dict["ephemeralCert"]["cert"]
278+
try:
279+
ephemeral_cert: str = ret_dict["ephemeralCert"]["cert"]
280+
except KeyError as e:
281+
logger.error(f"KeyError in _get_ephemeral parsing generateEphemeralCert: {e}. Response dict: {ret_dict}")
282+
raise
275283

276284
# decode cert to read expiration
277285
x509 = load_pem_x509_certificate(

google/cloud/sql/connector/connection_info.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class ConnectionInfo:
6262

6363
conn_name: ConnectionName
6464
client_cert: str
65-
server_ca_cert: str
65+
server_ca_cert: str | None
6666
private_key: bytes
6767
ip_addrs: dict[str, Any]
6868
database_version: str
@@ -78,6 +78,10 @@ async def create_ssl_context(self, enable_iam_auth: bool = False) -> ssl.SSLCont
7878
# if SSL context is cached, use it
7979
if self.context is not None:
8080
return self.context
81+
82+
if self.server_ca_cert is None:
83+
raise ValueError("Cannot create SSL context: server CA certificate is missing.")
84+
8185
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
8286

8387
# update ssl.PROTOCOL_TLS_CLIENT default

0 commit comments

Comments
 (0)