Skip to content

Commit 4f257f9

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 4aaee21 commit 4f257f9

16 files changed

Lines changed: 1669 additions & 135 deletions

build.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ function write_e2e_env(){
150150

151151
}
152152

153+
## with_venv - runs a command with the venv activated
154+
function with_venv() {
155+
"$@"
156+
}
157+
153158
## help - prints the help details
154159
##
155160
function help() {

google/cloud/sql/connector/asyncpg.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"""
1616

1717
import ssl
18-
from typing import Any, TYPE_CHECKING
18+
from typing import Any, Optional, TYPE_CHECKING
1919

2020
SERVER_PROXY_PORT = 3307
2121

@@ -24,16 +24,15 @@
2424

2525

2626
async def connect(
27-
ip_address: str, ctx: ssl.SSLContext, **kwargs: Any
27+
ip_address: str, ctx: Optional[ssl.SSLContext], **kwargs: Any
2828
) -> "asyncpg.Connection":
2929
"""Helper function to create an asyncpg DB-API connection object.
3030
3131
Args:
3232
ip_address (str): A string containing an IP address for the Cloud SQL
3333
instance.
3434
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.
35+
server CA cert and ephemeral cert. Pass None to disable SSL.
3736
kwargs: Keyword arguments for establishing asyncpg connection
3837
object to Cloud SQL instance.
3938
@@ -53,14 +52,18 @@ async def connect(
5352
user = kwargs.pop("user")
5453
db = kwargs.pop("db")
5554
passwd = kwargs.pop("password", None)
55+
port = kwargs.pop("port", SERVER_PROXY_PORT)
5656

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,
57+
connect_args = {
58+
"user": user,
59+
"database": db,
60+
"password": passwd,
61+
"host": ip_address,
62+
"port": port,
6563
**kwargs,
66-
)
64+
}
65+
if ctx is not None:
66+
connect_args["ssl"] = ctx
67+
connect_args["direct_tls"] = True
68+
69+
return await asyncpg.connect(**connect_args)

google/cloud/sql/connector/client.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,13 @@ async def _get_metadata(
171171
if dns_name:
172172
ip_addresses["PSC"] = dns_name.rstrip(".")
173173

174+
server_ca_cert = None
175+
if "serverCaCert" in ret_dict and "cert" in ret_dict["serverCaCert"]:
176+
server_ca_cert = ret_dict["serverCaCert"]["cert"]
177+
174178
return {
175179
"ip_addresses": ip_addresses,
176-
"server_ca_cert": ret_dict["serverCaCert"]["cert"],
180+
"server_ca_cert": server_ca_cert,
177181
"database_version": ret_dict["databaseVersion"],
178182
}
179183

@@ -204,7 +208,8 @@ async def _get_ephemeral(
204208

205209
url = f"{self._sqladmin_api_endpoint}/sql/{API_VERSION}/projects/{project}/instances/{instance}:generateEphemeralCert"
206210

207-
data = {"public_key": pub_key}
211+
pub_key_mtls = pub_key.replace("BEGIN PUBLIC KEY", "BEGIN RSA PUBLIC KEY").replace("END PUBLIC KEY", "END RSA PUBLIC KEY")
212+
data = {"public_key": pub_key_mtls, "access_token": self._credentials.token}
208213

209214
if enable_iam_auth:
210215
# down-scope credentials with only IAM login scope (refreshes them too)
@@ -228,7 +233,11 @@ async def _get_ephemeral(
228233
finally:
229234
resp.raise_for_status()
230235

231-
ephemeral_cert: str = ret_dict["ephemeralCert"]["cert"]
236+
try:
237+
ephemeral_cert: str = ret_dict["ephemeralCert"]["cert"]
238+
except KeyError as e:
239+
logger.error(f"KeyError in _get_ephemeral parsing generateEphemeralCert: {e}. Response dict: {ret_dict}")
240+
raise
232241

233242
# decode cert to read expiration
234243
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
@@ -63,7 +63,7 @@ class ConnectionInfo:
6363

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

8488
# update ssl.PROTOCOL_TLS_CLIENT default

0 commit comments

Comments
 (0)