cloud-sql-python-connector 1.20.4__tar.gz → 1.22.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/PKG-INFO +25 -4
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/README.md +19 -2
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/cloud_sql_python_connector.egg-info/PKG-INFO +25 -4
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/cloud_sql_python_connector.egg-info/SOURCES.txt +1 -0
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/cloud_sql_python_connector.egg-info/requires.txt +6 -1
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/asyncpg.py +3 -1
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/client.py +61 -19
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/connection_info.py +15 -5
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/connection_name.py +2 -4
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/connector.py +95 -62
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/enums.py +2 -1
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/exceptions.py +6 -6
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/instance.py +1 -1
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/lazy.py +5 -5
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/monitored_cache.py +42 -11
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/pg8000.py +3 -1
- cloud_sql_python_connector-1.22.0/google/cloud/sql/connector/psycopg.py +235 -0
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/pymysql.py +8 -1
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/pytds.py +1 -1
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/rate_limiter.py +3 -3
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/refresh_utils.py +4 -2
- cloud_sql_python_connector-1.22.0/google/cloud/sql/connector/resolver.py +173 -0
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/utils.py +40 -8
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/version.py +1 -1
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/pyproject.toml +4 -1
- cloud_sql_python_connector-1.20.4/google/cloud/sql/connector/resolver.py +0 -91
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/LICENSE +0 -0
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/cloud_sql_python_connector.egg-info/dependency_links.txt +0 -0
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/cloud_sql_python_connector.egg-info/top_level.txt +0 -0
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/__init__.py +2 -2
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/google/cloud/sql/connector/py.typed +0 -0
- {cloud_sql_python_connector-1.20.4 → cloud_sql_python_connector-1.22.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloud-sql-python-connector
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.22.0
|
|
4
4
|
Summary: Google Cloud SQL Python Connector library
|
|
5
5
|
Author-email: Google LLC <googleapis-packages@google.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -21,12 +21,14 @@ Classifier: Operating System :: OS Independent
|
|
|
21
21
|
Requires-Python: >=3.9
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE
|
|
24
|
-
Requires-Dist: aiofiles
|
|
25
24
|
Requires-Dist: aiohttp
|
|
26
25
|
Requires-Dist: cryptography>=42.0.0
|
|
27
26
|
Requires-Dist: dnspython>=2.0.0
|
|
28
27
|
Requires-Dist: Requests
|
|
29
28
|
Requires-Dist: google-auth>=2.28.0
|
|
29
|
+
Requires-Dist: grpcio
|
|
30
|
+
Requires-Dist: protobuf
|
|
31
|
+
Requires-Dist: googleapis-common-protos
|
|
30
32
|
Provides-Extra: pymysql
|
|
31
33
|
Requires-Dist: PyMySQL>=1.1.0; extra == "pymysql"
|
|
32
34
|
Provides-Extra: pg8000
|
|
@@ -35,6 +37,8 @@ Provides-Extra: pytds
|
|
|
35
37
|
Requires-Dist: python-tds>=1.15.0; extra == "pytds"
|
|
36
38
|
Provides-Extra: asyncpg
|
|
37
39
|
Requires-Dist: asyncpg>=0.30.0; extra == "asyncpg"
|
|
40
|
+
Provides-Extra: psycopg
|
|
41
|
+
Requires-Dist: psycopg>=3.1.0; extra == "psycopg"
|
|
38
42
|
Dynamic: license-file
|
|
39
43
|
|
|
40
44
|
<p align="center">
|
|
@@ -80,7 +84,8 @@ providing the following benefits:
|
|
|
80
84
|
The Cloud SQL Python Connector is a package to be used alongside a database driver.
|
|
81
85
|
Currently supported drivers are:
|
|
82
86
|
- [`pymysql`](https://github.com/PyMySQL/PyMySQL) (MySQL)
|
|
83
|
-
- [`pg8000`](https://
|
|
87
|
+
- [`pg8000`](https://codeberg.org/tlocke/pg8000) (PostgreSQL)
|
|
88
|
+
- [`psycopg`](https://github.com/psycopg/psycopg) (PostgreSQL)
|
|
84
89
|
- [`asyncpg`](https://github.com/MagicStack/asyncpg) (PostgreSQL)
|
|
85
90
|
- [`pytds`](https://github.com/denisenkom/pytds) (SQL Server)
|
|
86
91
|
|
|
@@ -95,12 +100,16 @@ based on your database dialect.
|
|
|
95
100
|
pip install "cloud-sql-python-connector[pymysql]"
|
|
96
101
|
```
|
|
97
102
|
### Postgres
|
|
98
|
-
There are
|
|
103
|
+
There are three different database drivers that are supported for the Postgres dialect:
|
|
99
104
|
|
|
100
105
|
#### pg8000
|
|
101
106
|
```
|
|
102
107
|
pip install "cloud-sql-python-connector[pg8000]"
|
|
103
108
|
```
|
|
109
|
+
#### psycopg
|
|
110
|
+
```
|
|
111
|
+
pip install "cloud-sql-python-connector[psycopg]"
|
|
112
|
+
```
|
|
104
113
|
#### asyncpg
|
|
105
114
|
```
|
|
106
115
|
pip install "cloud-sql-python-connector[asyncpg]"
|
|
@@ -176,6 +185,18 @@ pool = sqlalchemy.create_engine(
|
|
|
176
185
|
db="my-db-name"
|
|
177
186
|
),
|
|
178
187
|
)
|
|
188
|
+
|
|
189
|
+
# Or with Postgres (psycopg):
|
|
190
|
+
pool = sqlalchemy.create_engine(
|
|
191
|
+
"postgresql+psycopg://",
|
|
192
|
+
creator=lambda: connector.connect(
|
|
193
|
+
"project:region:instance",
|
|
194
|
+
"psycopg",
|
|
195
|
+
user="my-user",
|
|
196
|
+
password="my-password",
|
|
197
|
+
db="my-db-name"
|
|
198
|
+
),
|
|
199
|
+
)
|
|
179
200
|
```
|
|
180
201
|
|
|
181
202
|
The returned connection pool engine can then be used to query and modify the database.
|
|
@@ -41,7 +41,8 @@ providing the following benefits:
|
|
|
41
41
|
The Cloud SQL Python Connector is a package to be used alongside a database driver.
|
|
42
42
|
Currently supported drivers are:
|
|
43
43
|
- [`pymysql`](https://github.com/PyMySQL/PyMySQL) (MySQL)
|
|
44
|
-
- [`pg8000`](https://
|
|
44
|
+
- [`pg8000`](https://codeberg.org/tlocke/pg8000) (PostgreSQL)
|
|
45
|
+
- [`psycopg`](https://github.com/psycopg/psycopg) (PostgreSQL)
|
|
45
46
|
- [`asyncpg`](https://github.com/MagicStack/asyncpg) (PostgreSQL)
|
|
46
47
|
- [`pytds`](https://github.com/denisenkom/pytds) (SQL Server)
|
|
47
48
|
|
|
@@ -56,12 +57,16 @@ based on your database dialect.
|
|
|
56
57
|
pip install "cloud-sql-python-connector[pymysql]"
|
|
57
58
|
```
|
|
58
59
|
### Postgres
|
|
59
|
-
There are
|
|
60
|
+
There are three different database drivers that are supported for the Postgres dialect:
|
|
60
61
|
|
|
61
62
|
#### pg8000
|
|
62
63
|
```
|
|
63
64
|
pip install "cloud-sql-python-connector[pg8000]"
|
|
64
65
|
```
|
|
66
|
+
#### psycopg
|
|
67
|
+
```
|
|
68
|
+
pip install "cloud-sql-python-connector[psycopg]"
|
|
69
|
+
```
|
|
65
70
|
#### asyncpg
|
|
66
71
|
```
|
|
67
72
|
pip install "cloud-sql-python-connector[asyncpg]"
|
|
@@ -137,6 +142,18 @@ pool = sqlalchemy.create_engine(
|
|
|
137
142
|
db="my-db-name"
|
|
138
143
|
),
|
|
139
144
|
)
|
|
145
|
+
|
|
146
|
+
# Or with Postgres (psycopg):
|
|
147
|
+
pool = sqlalchemy.create_engine(
|
|
148
|
+
"postgresql+psycopg://",
|
|
149
|
+
creator=lambda: connector.connect(
|
|
150
|
+
"project:region:instance",
|
|
151
|
+
"psycopg",
|
|
152
|
+
user="my-user",
|
|
153
|
+
password="my-password",
|
|
154
|
+
db="my-db-name"
|
|
155
|
+
),
|
|
156
|
+
)
|
|
140
157
|
```
|
|
141
158
|
|
|
142
159
|
The returned connection pool engine can then be used to query and modify the database.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloud-sql-python-connector
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.22.0
|
|
4
4
|
Summary: Google Cloud SQL Python Connector library
|
|
5
5
|
Author-email: Google LLC <googleapis-packages@google.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -21,12 +21,14 @@ Classifier: Operating System :: OS Independent
|
|
|
21
21
|
Requires-Python: >=3.9
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE
|
|
24
|
-
Requires-Dist: aiofiles
|
|
25
24
|
Requires-Dist: aiohttp
|
|
26
25
|
Requires-Dist: cryptography>=42.0.0
|
|
27
26
|
Requires-Dist: dnspython>=2.0.0
|
|
28
27
|
Requires-Dist: Requests
|
|
29
28
|
Requires-Dist: google-auth>=2.28.0
|
|
29
|
+
Requires-Dist: grpcio
|
|
30
|
+
Requires-Dist: protobuf
|
|
31
|
+
Requires-Dist: googleapis-common-protos
|
|
30
32
|
Provides-Extra: pymysql
|
|
31
33
|
Requires-Dist: PyMySQL>=1.1.0; extra == "pymysql"
|
|
32
34
|
Provides-Extra: pg8000
|
|
@@ -35,6 +37,8 @@ Provides-Extra: pytds
|
|
|
35
37
|
Requires-Dist: python-tds>=1.15.0; extra == "pytds"
|
|
36
38
|
Provides-Extra: asyncpg
|
|
37
39
|
Requires-Dist: asyncpg>=0.30.0; extra == "asyncpg"
|
|
40
|
+
Provides-Extra: psycopg
|
|
41
|
+
Requires-Dist: psycopg>=3.1.0; extra == "psycopg"
|
|
38
42
|
Dynamic: license-file
|
|
39
43
|
|
|
40
44
|
<p align="center">
|
|
@@ -80,7 +84,8 @@ providing the following benefits:
|
|
|
80
84
|
The Cloud SQL Python Connector is a package to be used alongside a database driver.
|
|
81
85
|
Currently supported drivers are:
|
|
82
86
|
- [`pymysql`](https://github.com/PyMySQL/PyMySQL) (MySQL)
|
|
83
|
-
- [`pg8000`](https://
|
|
87
|
+
- [`pg8000`](https://codeberg.org/tlocke/pg8000) (PostgreSQL)
|
|
88
|
+
- [`psycopg`](https://github.com/psycopg/psycopg) (PostgreSQL)
|
|
84
89
|
- [`asyncpg`](https://github.com/MagicStack/asyncpg) (PostgreSQL)
|
|
85
90
|
- [`pytds`](https://github.com/denisenkom/pytds) (SQL Server)
|
|
86
91
|
|
|
@@ -95,12 +100,16 @@ based on your database dialect.
|
|
|
95
100
|
pip install "cloud-sql-python-connector[pymysql]"
|
|
96
101
|
```
|
|
97
102
|
### Postgres
|
|
98
|
-
There are
|
|
103
|
+
There are three different database drivers that are supported for the Postgres dialect:
|
|
99
104
|
|
|
100
105
|
#### pg8000
|
|
101
106
|
```
|
|
102
107
|
pip install "cloud-sql-python-connector[pg8000]"
|
|
103
108
|
```
|
|
109
|
+
#### psycopg
|
|
110
|
+
```
|
|
111
|
+
pip install "cloud-sql-python-connector[psycopg]"
|
|
112
|
+
```
|
|
104
113
|
#### asyncpg
|
|
105
114
|
```
|
|
106
115
|
pip install "cloud-sql-python-connector[asyncpg]"
|
|
@@ -176,6 +185,18 @@ pool = sqlalchemy.create_engine(
|
|
|
176
185
|
db="my-db-name"
|
|
177
186
|
),
|
|
178
187
|
)
|
|
188
|
+
|
|
189
|
+
# Or with Postgres (psycopg):
|
|
190
|
+
pool = sqlalchemy.create_engine(
|
|
191
|
+
"postgresql+psycopg://",
|
|
192
|
+
creator=lambda: connector.connect(
|
|
193
|
+
"project:region:instance",
|
|
194
|
+
"psycopg",
|
|
195
|
+
user="my-user",
|
|
196
|
+
password="my-password",
|
|
197
|
+
db="my-db-name"
|
|
198
|
+
),
|
|
199
|
+
)
|
|
179
200
|
```
|
|
180
201
|
|
|
181
202
|
The returned connection pool engine can then be used to query and modify the database.
|
|
@@ -18,6 +18,7 @@ google/cloud/sql/connector/instance.py
|
|
|
18
18
|
google/cloud/sql/connector/lazy.py
|
|
19
19
|
google/cloud/sql/connector/monitored_cache.py
|
|
20
20
|
google/cloud/sql/connector/pg8000.py
|
|
21
|
+
google/cloud/sql/connector/psycopg.py
|
|
21
22
|
google/cloud/sql/connector/py.typed
|
|
22
23
|
google/cloud/sql/connector/pymysql.py
|
|
23
24
|
google/cloud/sql/connector/pytds.py
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
aiofiles
|
|
2
1
|
aiohttp
|
|
3
2
|
cryptography>=42.0.0
|
|
4
3
|
dnspython>=2.0.0
|
|
5
4
|
Requests
|
|
6
5
|
google-auth>=2.28.0
|
|
6
|
+
grpcio
|
|
7
|
+
protobuf
|
|
8
|
+
googleapis-common-protos
|
|
7
9
|
|
|
8
10
|
[asyncpg]
|
|
9
11
|
asyncpg>=0.30.0
|
|
@@ -11,6 +13,9 @@ asyncpg>=0.30.0
|
|
|
11
13
|
[pg8000]
|
|
12
14
|
pg8000>=1.31.1
|
|
13
15
|
|
|
16
|
+
[psycopg]
|
|
17
|
+
psycopg>=3.1.0
|
|
18
|
+
|
|
14
19
|
[pymysql]
|
|
15
20
|
PyMySQL>=1.1.0
|
|
16
21
|
|
|
@@ -51,7 +51,9 @@ async def connect(
|
|
|
51
51
|
'Unable to import module "asyncpg." Please install and try again.'
|
|
52
52
|
)
|
|
53
53
|
user = kwargs.pop("user")
|
|
54
|
-
db = kwargs.pop("db")
|
|
54
|
+
db = kwargs.pop("database", kwargs.pop("db", None))
|
|
55
|
+
if db is None:
|
|
56
|
+
raise KeyError("database")
|
|
55
57
|
passwd = kwargs.pop("password", None)
|
|
56
58
|
|
|
57
59
|
return await asyncpg.connect(
|
|
@@ -17,7 +17,7 @@ from __future__ import annotations
|
|
|
17
17
|
import asyncio
|
|
18
18
|
import datetime
|
|
19
19
|
import logging
|
|
20
|
-
from typing import Any,
|
|
20
|
+
from typing import Any, TYPE_CHECKING
|
|
21
21
|
|
|
22
22
|
import aiohttp
|
|
23
23
|
from cryptography.hazmat.backends import default_backend
|
|
@@ -42,7 +42,7 @@ DEFAULT_SERVICE_ENDPOINT: str = "https://sqladmin.googleapis.com"
|
|
|
42
42
|
logger = logging.getLogger(name=__name__)
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
def _format_user_agent(driver:
|
|
45
|
+
def _format_user_agent(driver: str | None, custom: str | None) -> str:
|
|
46
46
|
agent = f"{USER_AGENT}+{driver}" if driver else USER_AGENT
|
|
47
47
|
if custom and isinstance(custom, str):
|
|
48
48
|
agent = f"{agent} {custom}"
|
|
@@ -52,12 +52,12 @@ def _format_user_agent(driver: Optional[str], custom: Optional[str]) -> str:
|
|
|
52
52
|
class CloudSQLClient:
|
|
53
53
|
def __init__(
|
|
54
54
|
self,
|
|
55
|
-
sqladmin_api_endpoint:
|
|
56
|
-
quota_project:
|
|
55
|
+
sqladmin_api_endpoint: str | None,
|
|
56
|
+
quota_project: str | None,
|
|
57
57
|
credentials: Credentials,
|
|
58
|
-
client:
|
|
59
|
-
driver:
|
|
60
|
-
user_agent:
|
|
58
|
+
client: aiohttp.ClientSession | None = None,
|
|
59
|
+
driver: str | None = None,
|
|
60
|
+
user_agent: str | None = None,
|
|
61
61
|
) -> None:
|
|
62
62
|
"""Establishes the client to be used for Cloud SQL Admin API requests.
|
|
63
63
|
|
|
@@ -135,7 +135,7 @@ class CloudSQLClient:
|
|
|
135
135
|
if message:
|
|
136
136
|
resp.reason = message
|
|
137
137
|
# skip, raise_for_status will catch all errors in finally block
|
|
138
|
-
except Exception:
|
|
138
|
+
except Exception: # noqa: BLE001, S110
|
|
139
139
|
pass
|
|
140
140
|
finally:
|
|
141
141
|
resp.raise_for_status()
|
|
@@ -146,7 +146,7 @@ class CloudSQLClient:
|
|
|
146
146
|
)
|
|
147
147
|
|
|
148
148
|
ip_addresses = (
|
|
149
|
-
{ip["type"]: ip["ipAddress"] for ip in ret_dict["ipAddresses"]}
|
|
149
|
+
{ip["type"]: [ip["ipAddress"]] for ip in ret_dict["ipAddresses"]}
|
|
150
150
|
if "ipAddresses" in ret_dict
|
|
151
151
|
else {}
|
|
152
152
|
)
|
|
@@ -156,20 +156,22 @@ class CloudSQLClient:
|
|
|
156
156
|
if ret_dict.get("pscEnabled"):
|
|
157
157
|
# Find PSC instance DNS name in the dns_names field
|
|
158
158
|
psc_dns_names = [
|
|
159
|
-
d["name"]
|
|
159
|
+
d["name"].rstrip(".")
|
|
160
160
|
for d in ret_dict.get("dnsNames", [])
|
|
161
161
|
if d["connectionType"] == "PRIVATE_SERVICE_CONNECT"
|
|
162
162
|
and d["dnsScope"] == "INSTANCE"
|
|
163
163
|
]
|
|
164
|
-
|
|
164
|
+
# Sort: .sql-psc.goog first
|
|
165
|
+
psc_dns_names.sort(key=lambda x: x.endswith(".sql-psc.goog"), reverse=True)
|
|
165
166
|
|
|
166
167
|
# Fall back do dns_name field if dns_names is not set
|
|
167
|
-
if
|
|
168
|
+
if not psc_dns_names:
|
|
168
169
|
dns_name = ret_dict.get("dnsName", None)
|
|
170
|
+
if dns_name:
|
|
171
|
+
psc_dns_names = [dns_name.rstrip(".")]
|
|
169
172
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
ip_addresses["PSC"] = dns_name.rstrip(".")
|
|
173
|
+
if psc_dns_names:
|
|
174
|
+
ip_addresses["PSC"] = psc_dns_names
|
|
173
175
|
|
|
174
176
|
return {
|
|
175
177
|
"ip_addresses": ip_addresses,
|
|
@@ -177,6 +179,47 @@ class CloudSQLClient:
|
|
|
177
179
|
"database_version": ret_dict["databaseVersion"],
|
|
178
180
|
}
|
|
179
181
|
|
|
182
|
+
async def resolve_connect_settings(
|
|
183
|
+
self,
|
|
184
|
+
dns_name: str,
|
|
185
|
+
location: str,
|
|
186
|
+
) -> dict[str, Any]:
|
|
187
|
+
"""Asynchronously calls the resolveConnectSettings endpoint to resolve a
|
|
188
|
+
PSC DNS name to a connection name.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
dns_name (str): The DNS name of the Cloud SQL instance.
|
|
192
|
+
location (str): The region/location of the instance.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
A dictionary containing the resolve response (e.g. connectionName).
|
|
196
|
+
"""
|
|
197
|
+
# before making Cloud SQL Admin API calls, refresh creds if required
|
|
198
|
+
if self._credentials.token_state != TokenState.FRESH:
|
|
199
|
+
self._credentials.refresh(requests.Request())
|
|
200
|
+
|
|
201
|
+
headers = {
|
|
202
|
+
"Authorization": f"Bearer {self._credentials.token}",
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
url = f"{self._sqladmin_api_endpoint}/sql/{API_VERSION}/locations/{location}/dns/{dns_name}:resolveConnectSettings"
|
|
206
|
+
|
|
207
|
+
resp = await self._client.get(url, headers=headers)
|
|
208
|
+
if resp.status >= 500:
|
|
209
|
+
resp = await retry_50x(self._client.get, url, headers=headers)
|
|
210
|
+
try:
|
|
211
|
+
ret_dict = await resp.json()
|
|
212
|
+
if resp.status >= 400:
|
|
213
|
+
message = ret_dict.get("error", {}).get("message")
|
|
214
|
+
if message:
|
|
215
|
+
resp.reason = message
|
|
216
|
+
except Exception: # noqa: BLE001, S110
|
|
217
|
+
pass
|
|
218
|
+
finally:
|
|
219
|
+
resp.raise_for_status()
|
|
220
|
+
|
|
221
|
+
return ret_dict
|
|
222
|
+
|
|
180
223
|
async def _get_ephemeral(
|
|
181
224
|
self,
|
|
182
225
|
project: str,
|
|
@@ -223,7 +266,7 @@ class CloudSQLClient:
|
|
|
223
266
|
if message:
|
|
224
267
|
resp.reason = message
|
|
225
268
|
# skip, raise_for_status will catch all errors in finally block
|
|
226
|
-
except Exception:
|
|
269
|
+
except Exception: # noqa: BLE001, S110
|
|
227
270
|
pass
|
|
228
271
|
finally:
|
|
229
272
|
resp.raise_for_status()
|
|
@@ -244,8 +287,7 @@ class CloudSQLClient:
|
|
|
244
287
|
# Ref: https://github.com/googleapis/google-auth-library-python/blob/49a5ff7411a2ae4d32a7d11700f9f961c55406a9/google/auth/_helpers.py#L93-L99
|
|
245
288
|
token_expiration = token_expiration.replace(tzinfo=datetime.timezone.utc)
|
|
246
289
|
|
|
247
|
-
|
|
248
|
-
expiration = token_expiration
|
|
290
|
+
expiration = min(expiration, token_expiration)
|
|
249
291
|
return ephemeral_cert, expiration
|
|
250
292
|
|
|
251
293
|
async def get_connection_info(
|
|
@@ -274,7 +316,7 @@ class CloudSQLClient:
|
|
|
274
316
|
"""
|
|
275
317
|
priv_key, pub_key = await keys
|
|
276
318
|
# before making Cloud SQL Admin API calls, refresh creds if required
|
|
277
|
-
if
|
|
319
|
+
if self._credentials.token_state != TokenState.FRESH:
|
|
278
320
|
self._credentials.refresh(requests.Request())
|
|
279
321
|
|
|
280
322
|
metadata_task = asyncio.create_task(
|
|
@@ -18,13 +18,12 @@ import abc
|
|
|
18
18
|
from dataclasses import dataclass
|
|
19
19
|
import logging
|
|
20
20
|
import ssl
|
|
21
|
-
from typing import Any,
|
|
22
|
-
|
|
23
|
-
from aiofiles.tempfile import TemporaryDirectory
|
|
21
|
+
from typing import Any, TYPE_CHECKING
|
|
24
22
|
|
|
25
23
|
from google.cloud.sql.connector.connection_name import ConnectionName
|
|
26
24
|
from google.cloud.sql.connector.exceptions import CloudSQLIPTypeError
|
|
27
25
|
from google.cloud.sql.connector.exceptions import TLSVersionError
|
|
26
|
+
from google.cloud.sql.connector.utils import AsyncTemporaryDirectory
|
|
28
27
|
from google.cloud.sql.connector.utils import write_to_file
|
|
29
28
|
|
|
30
29
|
if TYPE_CHECKING:
|
|
@@ -68,7 +67,7 @@ class ConnectionInfo:
|
|
|
68
67
|
ip_addrs: dict[str, Any]
|
|
69
68
|
database_version: str
|
|
70
69
|
expiration: datetime.datetime
|
|
71
|
-
context:
|
|
70
|
+
context: ssl.SSLContext | None = None
|
|
72
71
|
|
|
73
72
|
async def create_ssl_context(self, enable_iam_auth: bool = False) -> ssl.SSLContext:
|
|
74
73
|
"""Constructs a SSL/TLS context for the given connection info.
|
|
@@ -108,7 +107,7 @@ class ConnectionInfo:
|
|
|
108
107
|
# tmpdir and its contents are automatically deleted after the CA cert
|
|
109
108
|
# and ephemeral cert are loaded into the SSLcontext. The values
|
|
110
109
|
# need to be written to files in order to be loaded by the SSLContext
|
|
111
|
-
async with
|
|
110
|
+
async with AsyncTemporaryDirectory() as tmpdir:
|
|
112
111
|
ca_filename, cert_filename, key_filename = await write_to_file(
|
|
113
112
|
tmpdir, self.server_ca_cert, self.client_cert, self.private_key
|
|
114
113
|
)
|
|
@@ -122,6 +121,17 @@ class ConnectionInfo:
|
|
|
122
121
|
"""Returns the first IP address for the instance, according to the preference
|
|
123
122
|
supplied by ip_type. If no IP addressess with the given preference are found,
|
|
124
123
|
an error is raised."""
|
|
124
|
+
if ip_type.value in self.ip_addrs:
|
|
125
|
+
return self.ip_addrs[ip_type.value][0]
|
|
126
|
+
raise CloudSQLIPTypeError(
|
|
127
|
+
"Cloud SQL instance does not have any IP addresses matching "
|
|
128
|
+
f"preference: {ip_type.value}"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
def get_preferred_ips(self, ip_type: IPTypes) -> list[str]:
|
|
132
|
+
"""Returns all IP addresses for the instance, according to the preference
|
|
133
|
+
supplied by ip_type. If no IP addressess with the given preference are found,
|
|
134
|
+
an error is raised."""
|
|
125
135
|
if ip_type.value in self.ip_addrs:
|
|
126
136
|
return self.ip_addrs[ip_type.value]
|
|
127
137
|
raise CloudSQLIPTypeError(
|
|
@@ -18,7 +18,7 @@ import re
|
|
|
18
18
|
# Instance connection name is the format <PROJECT>:<REGION>:<INSTANCE_NAME>
|
|
19
19
|
# Additionally, we have to support legacy "domain-scoped" projects
|
|
20
20
|
# (e.g. "google.com:PROJECT")
|
|
21
|
-
CONN_NAME_REGEX = re.compile(
|
|
21
|
+
CONN_NAME_REGEX = re.compile("([^:]+(:[^:]+)?):([^:]+):([^:]+)")
|
|
22
22
|
# The domain name pattern in accordance with RFC 1035, RFC 1123 and RFC 2181.
|
|
23
23
|
DOMAIN_NAME_REGEX = re.compile(
|
|
24
24
|
r"^(?:[_a-z0-9](?:[_a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?)?$"
|
|
@@ -48,9 +48,7 @@ class ConnectionName:
|
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
def _is_valid_domain(domain_name: str) -> bool:
|
|
51
|
-
|
|
52
|
-
return False
|
|
53
|
-
return True
|
|
51
|
+
return DOMAIN_NAME_REGEX.fullmatch(domain_name) is not None
|
|
54
52
|
|
|
55
53
|
|
|
56
54
|
def _parse_connection_name(connection_name: str) -> ConnectionName:
|