cloud-sql-python-connector 1.17.0.dev0__py3-none-any.whl → 1.18.1__py3-none-any.whl
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.17.0.dev0.dist-info → cloud_sql_python_connector-1.18.1.dist-info}/METADATA +103 -130
- cloud_sql_python_connector-1.18.1.dist-info/RECORD +25 -0
- {cloud_sql_python_connector-1.17.0.dev0.dist-info → cloud_sql_python_connector-1.18.1.dist-info}/WHEEL +1 -1
- google/cloud/sql/connector/__init__.py +1 -1
- google/cloud/sql/connector/asyncpg.py +14 -13
- google/cloud/sql/connector/client.py +41 -37
- google/cloud/sql/connector/connection_info.py +22 -0
- google/cloud/sql/connector/connection_name.py +14 -0
- google/cloud/sql/connector/connector.py +50 -15
- google/cloud/sql/connector/exceptions.py +7 -0
- google/cloud/sql/connector/instance.py +12 -1
- google/cloud/sql/connector/lazy.py +13 -2
- google/cloud/sql/connector/monitored_cache.py +146 -0
- google/cloud/sql/connector/pg8000.py +12 -19
- google/cloud/sql/connector/pymysql.py +11 -17
- google/cloud/sql/connector/pytds.py +11 -18
- google/cloud/sql/connector/rate_limiter.py +9 -17
- google/cloud/sql/connector/refresh_utils.py +12 -11
- google/cloud/sql/connector/resolver.py +11 -2
- google/cloud/sql/connector/utils.py +6 -8
- google/cloud/sql/connector/version.py +1 -1
- cloud_sql_python_connector-1.17.0.dev0.dist-info/RECORD +0 -24
- {cloud_sql_python_connector-1.17.0.dev0.dist-info → cloud_sql_python_connector-1.18.1.dist-info/licenses}/LICENSE +0 -0
- {cloud_sql_python_connector-1.17.0.dev0.dist-info → cloud_sql_python_connector-1.18.1.dist-info}/top_level.txt +0 -0
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: cloud-sql-python-connector
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.18.1
|
|
4
4
|
Summary: Google Cloud SQL Python Connector library
|
|
5
5
|
Author-email: Google LLC <googleapis-packages@google.com>
|
|
6
|
-
License: Apache
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
7
|
Project-URL: Homepage, https://github.com/GoogleCloudPlatform/cloud-sql-python-connector
|
|
8
8
|
Project-URL: Repository, https://github.com/GoogleCloudPlatform/cloud-sql-python-connector.git
|
|
9
9
|
Project-URL: Issues, https://github.com/GoogleCloudPlatform/cloud-sql-python-connector/issues
|
|
10
10
|
Project-URL: Changelog, https://github.com/GoogleCloudPlatform/cloud-sql-python-connector/blob/main/CHANGELOG.md
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
13
|
Classifier: Programming Language :: Python
|
|
15
14
|
Classifier: Programming Language :: Python :: 3
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.9
|
|
@@ -36,6 +35,7 @@ Provides-Extra: pytds
|
|
|
36
35
|
Requires-Dist: python-tds>=1.15.0; extra == "pytds"
|
|
37
36
|
Provides-Extra: asyncpg
|
|
38
37
|
Requires-Dist: asyncpg>=0.30.0; extra == "asyncpg"
|
|
38
|
+
Dynamic: license-file
|
|
39
39
|
|
|
40
40
|
<p align="center">
|
|
41
41
|
<a href="https://cloud.google.com/blog/topics/developers-practitioners/how-connect-cloud-sql-using-python-easy-way">
|
|
@@ -165,21 +165,16 @@ import sqlalchemy
|
|
|
165
165
|
# initialize Connector object
|
|
166
166
|
connector = Connector()
|
|
167
167
|
|
|
168
|
-
#
|
|
169
|
-
|
|
170
|
-
|
|
168
|
+
# initialize SQLAlchemy connection pool with Connector
|
|
169
|
+
pool = sqlalchemy.create_engine(
|
|
170
|
+
"mysql+pymysql://",
|
|
171
|
+
creator=lambda: connector.connect(
|
|
171
172
|
"project:region:instance",
|
|
172
173
|
"pymysql",
|
|
173
174
|
user="my-user",
|
|
174
175
|
password="my-password",
|
|
175
176
|
db="my-db-name"
|
|
176
|
-
)
|
|
177
|
-
return conn
|
|
178
|
-
|
|
179
|
-
# create connection pool
|
|
180
|
-
pool = sqlalchemy.create_engine(
|
|
181
|
-
"mysql+pymysql://",
|
|
182
|
-
creator=getconn,
|
|
177
|
+
),
|
|
183
178
|
)
|
|
184
179
|
```
|
|
185
180
|
|
|
@@ -246,33 +241,21 @@ Connector as a context manager:
|
|
|
246
241
|
|
|
247
242
|
```python
|
|
248
243
|
from google.cloud.sql.connector import Connector
|
|
249
|
-
import pymysql
|
|
250
244
|
import sqlalchemy
|
|
251
245
|
|
|
252
|
-
#
|
|
253
|
-
|
|
254
|
-
#
|
|
255
|
-
|
|
256
|
-
|
|
246
|
+
# initialize Cloud SQL Python Connector as context manager
|
|
247
|
+
with Connector() as connector:
|
|
248
|
+
# initialize SQLAlchemy connection pool with Connector
|
|
249
|
+
pool = sqlalchemy.create_engine(
|
|
250
|
+
"mysql+pymysql://",
|
|
251
|
+
creator=lambda: connector.connect(
|
|
257
252
|
"project:region:instance",
|
|
258
253
|
"pymysql",
|
|
259
254
|
user="my-user",
|
|
260
255
|
password="my-password",
|
|
261
256
|
db="my-db-name"
|
|
262
|
-
)
|
|
263
|
-
return conn
|
|
264
|
-
|
|
265
|
-
# create connection pool
|
|
266
|
-
pool = sqlalchemy.create_engine(
|
|
267
|
-
"mysql+pymysql://",
|
|
268
|
-
creator=getconn,
|
|
257
|
+
),
|
|
269
258
|
)
|
|
270
|
-
return pool
|
|
271
|
-
|
|
272
|
-
# initialize Cloud SQL Python Connector as context manager
|
|
273
|
-
with Connector() as connector:
|
|
274
|
-
# initialize connection pool
|
|
275
|
-
pool = init_connection_pool(connector)
|
|
276
259
|
# insert statement
|
|
277
260
|
insert_stmt = sqlalchemy.text(
|
|
278
261
|
"INSERT INTO my_table (id, title) VALUES (:id, :title)",
|
|
@@ -440,33 +423,60 @@ from google.cloud.sql.connector import Connector, DnsResolver
|
|
|
440
423
|
import pymysql
|
|
441
424
|
import sqlalchemy
|
|
442
425
|
|
|
443
|
-
#
|
|
444
|
-
|
|
445
|
-
#
|
|
446
|
-
|
|
447
|
-
|
|
426
|
+
# initialize Cloud SQL Python Connector with `resolver=DnsResolver`
|
|
427
|
+
with Connector(resolver=DnsResolver) as connector:
|
|
428
|
+
# initialize SQLAlchemy connection pool with Connector
|
|
429
|
+
pool = sqlalchemy.create_engine(
|
|
430
|
+
"mysql+pymysql://",
|
|
431
|
+
creator=lambda: connector.connect(
|
|
448
432
|
"prod-db.mycompany.example.com", # using DNS name
|
|
449
433
|
"pymysql",
|
|
450
434
|
user="my-user",
|
|
451
435
|
password="my-password",
|
|
452
436
|
db="my-db-name"
|
|
453
|
-
)
|
|
454
|
-
return conn
|
|
455
|
-
|
|
456
|
-
# create connection pool
|
|
457
|
-
pool = sqlalchemy.create_engine(
|
|
458
|
-
"mysql+pymysql://",
|
|
459
|
-
creator=getconn,
|
|
437
|
+
),
|
|
460
438
|
)
|
|
461
|
-
return pool
|
|
462
|
-
|
|
463
|
-
# initialize Cloud SQL Python Connector with `resolver=DnsResolver`
|
|
464
|
-
with Connector(resolver=DnsResolver) as connector:
|
|
465
|
-
# initialize connection pool
|
|
466
|
-
pool = init_connection_pool(connector)
|
|
467
439
|
# ... use SQLAlchemy engine normally
|
|
468
440
|
```
|
|
469
441
|
|
|
442
|
+
### Automatic failover using DNS domain names
|
|
443
|
+
|
|
444
|
+
> [!NOTE]
|
|
445
|
+
>
|
|
446
|
+
> Usage of the `asyncpg` driver does not currently support automatic failover.
|
|
447
|
+
|
|
448
|
+
When the connector is configured using a domain name, the connector will
|
|
449
|
+
periodically check if the DNS record for an instance changes. When the connector
|
|
450
|
+
detects that the domain name refers to a different instance, the connector will
|
|
451
|
+
close all open connections to the old instance. Subsequent connection attempts
|
|
452
|
+
will be directed to the new instance.
|
|
453
|
+
|
|
454
|
+
For example: suppose application is configured to connect using the
|
|
455
|
+
domain name `prod-db.mycompany.example.com`. Initially the private DNS
|
|
456
|
+
zone has a TXT record with the value `my-project:region:my-instance`. The
|
|
457
|
+
application establishes connections to the `my-project:region:my-instance`
|
|
458
|
+
Cloud SQL instance.
|
|
459
|
+
|
|
460
|
+
Then, to reconfigure the application to use a different database
|
|
461
|
+
instance, change the value of the `prod-db.mycompany.example.com` DNS record
|
|
462
|
+
from `my-project:region:my-instance` to `my-project:other-region:my-instance-2`
|
|
463
|
+
|
|
464
|
+
The connector inside the application detects the change to this
|
|
465
|
+
DNS record. Now, when the application connects to its database using the
|
|
466
|
+
domain name `prod-db.mycompany.example.com`, it will connect to the
|
|
467
|
+
`my-project:other-region:my-instance-2` Cloud SQL instance.
|
|
468
|
+
|
|
469
|
+
The connector will automatically close all existing connections to
|
|
470
|
+
`my-project:region:my-instance`. This will force the connection pools to
|
|
471
|
+
establish new connections. Also, it may cause database queries in progress
|
|
472
|
+
to fail.
|
|
473
|
+
|
|
474
|
+
The connector will poll for changes to the DNS name every 30 seconds by default.
|
|
475
|
+
You may configure the frequency of the connections using the Connector's
|
|
476
|
+
`failover_period` argument (i.e. `Connector(failover_period=60`). When this is
|
|
477
|
+
set to 0, the connector will disable polling and only check if the DNS record
|
|
478
|
+
changed when it is creating a new connection.
|
|
479
|
+
|
|
470
480
|
### Using the Python Connector with Python Web Frameworks
|
|
471
481
|
|
|
472
482
|
The Python Connector can be used alongside popular Python web frameworks such
|
|
@@ -502,9 +512,12 @@ from google.cloud.sql.connector import Connector
|
|
|
502
512
|
# initialize Python Connector object
|
|
503
513
|
connector = Connector()
|
|
504
514
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
515
|
+
app = Flask(__name__)
|
|
516
|
+
|
|
517
|
+
# configure Flask-SQLAlchemy to use Python Connector
|
|
518
|
+
app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql+pg8000://"
|
|
519
|
+
app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
|
|
520
|
+
"creator": lambda: conn = connector.connect(
|
|
508
521
|
"project:region:instance-name", # Cloud SQL Instance Connection Name
|
|
509
522
|
"pg8000",
|
|
510
523
|
user="my-user",
|
|
@@ -512,15 +525,6 @@ def getconn():
|
|
|
512
525
|
db="my-database",
|
|
513
526
|
ip_type="public" # "private" for private IP
|
|
514
527
|
)
|
|
515
|
-
return conn
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
app = Flask(__name__)
|
|
519
|
-
|
|
520
|
-
# configure Flask-SQLAlchemy to use Python Connector
|
|
521
|
-
app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql+pg8000://"
|
|
522
|
-
app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
|
|
523
|
-
"creator": getconn
|
|
524
528
|
}
|
|
525
529
|
|
|
526
530
|
# initialize the app with the extension
|
|
@@ -541,38 +545,27 @@ your web application using [SQLAlchemy ORM](https://docs.sqlalchemy.org/en/14/or
|
|
|
541
545
|
through the following:
|
|
542
546
|
|
|
543
547
|
```python
|
|
544
|
-
|
|
545
|
-
from sqlalchemy.engine import Engine
|
|
548
|
+
import sqlalchemy
|
|
546
549
|
from sqlalchemy.ext.declarative import declarative_base
|
|
547
550
|
from sqlalchemy.orm import sessionmaker
|
|
548
551
|
from google.cloud.sql.connector import Connector
|
|
549
552
|
|
|
550
|
-
# helper function to return SQLAlchemy connection pool
|
|
551
|
-
def init_connection_pool(connector: Connector) -> Engine:
|
|
552
|
-
# Python Connector database connection function
|
|
553
|
-
def getconn():
|
|
554
|
-
conn = connector.connect(
|
|
555
|
-
"project:region:instance-name", # Cloud SQL Instance Connection Name
|
|
556
|
-
"pg8000",
|
|
557
|
-
user="my-user",
|
|
558
|
-
password="my-password",
|
|
559
|
-
db="my-database",
|
|
560
|
-
ip_type="public" # "private" for private IP
|
|
561
|
-
)
|
|
562
|
-
return conn
|
|
563
|
-
|
|
564
|
-
SQLALCHEMY_DATABASE_URL = "postgresql+pg8000://"
|
|
565
|
-
|
|
566
|
-
engine = create_engine(
|
|
567
|
-
SQLALCHEMY_DATABASE_URL , creator=getconn
|
|
568
|
-
)
|
|
569
|
-
return engine
|
|
570
553
|
|
|
571
554
|
# initialize Cloud SQL Python Connector
|
|
572
555
|
connector = Connector()
|
|
573
556
|
|
|
574
557
|
# create connection pool engine
|
|
575
|
-
engine =
|
|
558
|
+
engine = sqlalchemy.create_engine(
|
|
559
|
+
"postgresql+pg8000://",
|
|
560
|
+
creator=lambda: connector.connect(
|
|
561
|
+
"project:region:instance-name", # Cloud SQL Instance Connection Name
|
|
562
|
+
"pg8000",
|
|
563
|
+
user="my-user",
|
|
564
|
+
password="my-password",
|
|
565
|
+
db="my-database",
|
|
566
|
+
ip_type="public" # "private" for private IP
|
|
567
|
+
),
|
|
568
|
+
)
|
|
576
569
|
|
|
577
570
|
# create SQLAlchemy ORM session
|
|
578
571
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
@@ -641,40 +634,29 @@ async def main():
|
|
|
641
634
|
#### SQLAlchemy Async Engine
|
|
642
635
|
|
|
643
636
|
```python
|
|
644
|
-
import asyncpg
|
|
645
|
-
|
|
646
637
|
import sqlalchemy
|
|
647
638
|
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
|
|
648
639
|
|
|
649
640
|
from google.cloud.sql.connector import Connector, create_async_connector
|
|
650
641
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
642
|
+
|
|
643
|
+
async def main():
|
|
644
|
+
# initialize Connector object for connections to Cloud SQL
|
|
645
|
+
connector = await create_async_connector()
|
|
646
|
+
|
|
647
|
+
# The Cloud SQL Python Connector can be used along with SQLAlchemy using the
|
|
648
|
+
# 'async_creator' argument to 'create_async_engine'
|
|
649
|
+
pool = create_async_engine(
|
|
650
|
+
"postgresql+asyncpg://",
|
|
651
|
+
async_creator=lambda: connector.connect_async(
|
|
655
652
|
"project:region:instance", # Cloud SQL instance connection name
|
|
656
653
|
"asyncpg",
|
|
657
654
|
user="my-user",
|
|
658
655
|
password="my-password",
|
|
659
656
|
db="my-db-name"
|
|
660
657
|
# ... additional database driver args
|
|
661
|
-
)
|
|
662
|
-
return conn
|
|
663
|
-
|
|
664
|
-
# The Cloud SQL Python Connector can be used along with SQLAlchemy using the
|
|
665
|
-
# 'async_creator' argument to 'create_async_engine'
|
|
666
|
-
pool = create_async_engine(
|
|
667
|
-
"postgresql+asyncpg://",
|
|
668
|
-
async_creator=getconn,
|
|
658
|
+
),
|
|
669
659
|
)
|
|
670
|
-
return pool
|
|
671
|
-
|
|
672
|
-
async def main():
|
|
673
|
-
# initialize Connector object for connections to Cloud SQL
|
|
674
|
-
connector = await create_async_connector()
|
|
675
|
-
|
|
676
|
-
# initialize connection pool
|
|
677
|
-
pool = await init_connection_pool(connector)
|
|
678
660
|
|
|
679
661
|
# example query
|
|
680
662
|
async with pool.connect() as conn:
|
|
@@ -745,33 +727,24 @@ from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
|
|
|
745
727
|
|
|
746
728
|
from google.cloud.sql.connector import Connector
|
|
747
729
|
|
|
748
|
-
async def init_connection_pool(connector: Connector) -> AsyncEngine:
|
|
749
|
-
# creation function to generate asyncpg connections as 'async_creator' arg
|
|
750
|
-
async def getconn() -> asyncpg.Connection:
|
|
751
|
-
conn: asyncpg.Connection = await connector.connect_async(
|
|
752
|
-
"project:region:instance", # Cloud SQL instance connection name
|
|
753
|
-
"asyncpg",
|
|
754
|
-
user="my-user",
|
|
755
|
-
password="my-password",
|
|
756
|
-
db="my-db-name"
|
|
757
|
-
# ... additional database driver args
|
|
758
|
-
)
|
|
759
|
-
return conn
|
|
760
|
-
|
|
761
|
-
# The Cloud SQL Python Connector can be used along with SQLAlchemy using the
|
|
762
|
-
# 'async_creator' argument to 'create_async_engine'
|
|
763
|
-
pool = create_async_engine(
|
|
764
|
-
"postgresql+asyncpg://",
|
|
765
|
-
async_creator=getconn,
|
|
766
|
-
)
|
|
767
|
-
return pool
|
|
768
730
|
|
|
769
731
|
async def main():
|
|
770
732
|
# initialize Connector object for connections to Cloud SQL
|
|
771
733
|
loop = asyncio.get_running_loop()
|
|
772
734
|
async with Connector(loop=loop) as connector:
|
|
773
|
-
#
|
|
774
|
-
|
|
735
|
+
# The Cloud SQL Python Connector can be used along with SQLAlchemy using the
|
|
736
|
+
# 'async_creator' argument to 'create_async_engine'
|
|
737
|
+
pool = create_async_engine(
|
|
738
|
+
"postgresql+asyncpg://",
|
|
739
|
+
async_creator=lambda: connector.connect_async(
|
|
740
|
+
"project:region:instance", # Cloud SQL instance connection name
|
|
741
|
+
"asyncpg",
|
|
742
|
+
user="my-user",
|
|
743
|
+
password="my-password",
|
|
744
|
+
db="my-db-name"
|
|
745
|
+
# ... additional database driver args
|
|
746
|
+
),
|
|
747
|
+
)
|
|
775
748
|
|
|
776
749
|
# example query
|
|
777
750
|
async with pool.connect() as conn:
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
cloud_sql_python_connector-1.18.1.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
2
|
+
google/cloud/sql/connector/__init__.py,sha256=w6w35V-m2QlpcL-ShKd2BvNF-xXOwJ2FoidE7npPE7c,1148
|
|
3
|
+
google/cloud/sql/connector/asyncpg.py,sha256=v4ohOnSYjHTOvJPhTE761xP1KKo4FUJQW_WAnNW_2g4,1918
|
|
4
|
+
google/cloud/sql/connector/client.py,sha256=uVOMnRe1AxiSh6BnK4-SuSYrRID_jnm7SgMMN6s5jp4,13076
|
|
5
|
+
google/cloud/sql/connector/connection_info.py,sha256=BSuW-ovtEUYTIvDzQkaE3GofCs3Xoi0TtztFaAq4Kvo,4868
|
|
6
|
+
google/cloud/sql/connector/connection_name.py,sha256=bI4M3Qa2mtBHyD08oF41FF5W9FBOXWu4dGGg50Na_A4,2606
|
|
7
|
+
google/cloud/sql/connector/connector.py,sha256=jukyYD75FElLvKgSHg4o91Wug21jIn87nJWNbeoTZUc,24183
|
|
8
|
+
google/cloud/sql/connector/enums.py,sha256=dVBS9eEWNrHtzze0AJlmtbdUogG3EocNWOQcI_8qPd0,2937
|
|
9
|
+
google/cloud/sql/connector/exceptions.py,sha256=QdQBzZ3t1RE5V744wW4sGaucWD2cEedB-dWNCkh8SAA,1974
|
|
10
|
+
google/cloud/sql/connector/instance.py,sha256=jDEQf-OXFzqKO9a4bnzRQ0qUyO3_xKuiEIRWc_7wpyE,8694
|
|
11
|
+
google/cloud/sql/connector/lazy.py,sha256=oB6F-O2fk8ZguY4J7Vrts91bqbVb6P5wj72Lf6UJIQA,4948
|
|
12
|
+
google/cloud/sql/connector/monitored_cache.py,sha256=LFYjX8hsdN_Dt0cMwLSWgAhWWQpZ35qA0CLlpc6xhsE,5587
|
|
13
|
+
google/cloud/sql/connector/pg8000.py,sha256=TnS_SNbgY19oNEawOx-qJyW-f_k42-SnR3VTBOSr5FI,1717
|
|
14
|
+
google/cloud/sql/connector/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
google/cloud/sql/connector/pymysql.py,sha256=PrTQ1lB5EWAcN4BPX_5hb-WerncS4X76bBk4y0es0ks,1948
|
|
16
|
+
google/cloud/sql/connector/pytds.py,sha256=KZxmJQQG9XfmsmUoEGX7RpMirWdoreJ2-_FuHO6Td-s,2280
|
|
17
|
+
google/cloud/sql/connector/rate_limiter.py,sha256=zqZ9pVXHan4LWoGT8Bq6U3HpZcrdzKhlXVYwsX3ftA0,2928
|
|
18
|
+
google/cloud/sql/connector/refresh_utils.py,sha256=Y6pT4X13b0IozmRMPuhQJRqacaGubn_mjzpFFjrXonM,5122
|
|
19
|
+
google/cloud/sql/connector/resolver.py,sha256=1KYN0E-oTca8wRyW2nqCw56XRHtq-QGYpPvkk-UhjnY,3290
|
|
20
|
+
google/cloud/sql/connector/utils.py,sha256=AA_oBBxRuULb7-hHHK8HQJU5fttzBpzdIZwMNZ5kNCM,3473
|
|
21
|
+
google/cloud/sql/connector/version.py,sha256=EN8WaWofBDH6G6c_3vog4YS3q3OoSo7ILwETmMo8ZT0,598
|
|
22
|
+
cloud_sql_python_connector-1.18.1.dist-info/METADATA,sha256=X6kdCzO2GF6gry0g9A7P8EJ1RNoTSHBJF5ft5y5PRH8,30340
|
|
23
|
+
cloud_sql_python_connector-1.18.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
24
|
+
cloud_sql_python_connector-1.18.1.dist-info/top_level.txt,sha256=_1QvSJIhFAGfxb79D6DhB7SUw2X6T4rwnz_LLrbcD3c,7
|
|
25
|
+
cloud_sql_python_connector-1.18.1.dist-info/RECORD,,
|
|
@@ -28,21 +28,22 @@ async def connect(
|
|
|
28
28
|
) -> "asyncpg.Connection":
|
|
29
29
|
"""Helper function to create an asyncpg DB-API connection object.
|
|
30
30
|
|
|
31
|
-
:
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
Args:
|
|
32
|
+
ip_address (str): A string containing an IP address for the Cloud SQL
|
|
33
|
+
instance.
|
|
34
|
+
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
|
+
kwargs: Keyword arguments for establishing asyncpg connection
|
|
38
|
+
object to Cloud SQL instance.
|
|
34
39
|
|
|
35
|
-
:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
:param kwargs: Keyword arguments for establishing asyncpg connection
|
|
41
|
-
object to Cloud SQL instance.
|
|
42
|
-
|
|
43
|
-
:rtype: asyncpg.Connection
|
|
44
|
-
:returns: An asyncpg.Connection object to a Cloud SQL instance.
|
|
40
|
+
Returns:
|
|
41
|
+
asyncpg.Connection: An asyncpg connection to the Cloud SQL
|
|
42
|
+
instance.
|
|
43
|
+
Raises:
|
|
44
|
+
ImportError: The asyncpg module cannot be imported.
|
|
45
45
|
"""
|
|
46
|
+
|
|
46
47
|
try:
|
|
47
48
|
import asyncpg
|
|
48
49
|
except ImportError:
|
|
@@ -59,8 +59,7 @@ class CloudSQLClient:
|
|
|
59
59
|
driver: Optional[str] = None,
|
|
60
60
|
user_agent: Optional[str] = None,
|
|
61
61
|
) -> None:
|
|
62
|
-
"""
|
|
63
|
-
Establish the client to be used for Cloud SQL Admin API requests.
|
|
62
|
+
"""Establishes the client to be used for Cloud SQL Admin API requests.
|
|
64
63
|
|
|
65
64
|
Args:
|
|
66
65
|
sqladmin_api_endpoint (str): Base URL to use when calling
|
|
@@ -100,24 +99,22 @@ class CloudSQLClient:
|
|
|
100
99
|
region: str,
|
|
101
100
|
instance: str,
|
|
102
101
|
) -> dict[str, Any]:
|
|
103
|
-
"""Requests metadata from the Cloud SQL Instance
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
:type project: str
|
|
108
|
-
:param project:
|
|
109
|
-
A string representing the name of the project.
|
|
102
|
+
"""Requests metadata from the Cloud SQL Instance and returns a dictionary
|
|
103
|
+
containing the IP addresses and certificate authority of the Cloud SQL
|
|
104
|
+
Instance.
|
|
110
105
|
|
|
111
|
-
:
|
|
112
|
-
|
|
106
|
+
Args:
|
|
107
|
+
project (str): A string representing the name of the project.
|
|
108
|
+
region (str): A string representing the name of the region.
|
|
109
|
+
instance (str): A string representing the name of the instance.
|
|
113
110
|
|
|
114
|
-
:
|
|
115
|
-
|
|
111
|
+
Returns:
|
|
112
|
+
A dictionary containing a dictionary of all IP addresses
|
|
113
|
+
and their type and a string representing the certificate authority.
|
|
116
114
|
|
|
117
|
-
:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
certificate authority.
|
|
115
|
+
Raises:
|
|
116
|
+
ValueError: Provided region does not match the region of the
|
|
117
|
+
Cloud SQL instance.
|
|
121
118
|
"""
|
|
122
119
|
|
|
123
120
|
headers = {
|
|
@@ -156,10 +153,23 @@ class CloudSQLClient:
|
|
|
156
153
|
# resolve dnsName into IP address for PSC
|
|
157
154
|
# Note that we have to check for PSC enablement also because CAS
|
|
158
155
|
# instances also set the dnsName field.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
156
|
+
if ret_dict.get("pscEnabled"):
|
|
157
|
+
# Find PSC instance DNS name in the dns_names field
|
|
158
|
+
psc_dns_names = [
|
|
159
|
+
d["name"]
|
|
160
|
+
for d in ret_dict.get("dnsNames", [])
|
|
161
|
+
if d["connectionType"] == "PRIVATE_SERVICE_CONNECT"
|
|
162
|
+
and d["dnsScope"] == "INSTANCE"
|
|
163
|
+
]
|
|
164
|
+
dns_name = psc_dns_names[0] if psc_dns_names else None
|
|
165
|
+
|
|
166
|
+
# Fall back do dns_name field if dns_names is not set
|
|
167
|
+
if dns_name is None:
|
|
168
|
+
dns_name = ret_dict.get("dnsName", None)
|
|
169
|
+
|
|
170
|
+
# Remove trailing period from DNS name. Required for SSL in Python
|
|
171
|
+
if dns_name:
|
|
172
|
+
ip_addresses["PSC"] = dns_name.rstrip(".")
|
|
163
173
|
|
|
164
174
|
return {
|
|
165
175
|
"ip_addresses": ip_addresses,
|
|
@@ -176,23 +186,17 @@ class CloudSQLClient:
|
|
|
176
186
|
) -> tuple[str, datetime.datetime]:
|
|
177
187
|
"""Asynchronously requests an ephemeral certificate from the Cloud SQL Instance.
|
|
178
188
|
|
|
179
|
-
:
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
:type pub_key:
|
|
186
|
-
:param str: A string representing PEM-encoded RSA public key.
|
|
187
|
-
|
|
188
|
-
:type enable_iam_auth: bool
|
|
189
|
-
:param enable_iam_auth
|
|
190
|
-
Enables automatic IAM database authentication for Postgres or MySQL
|
|
191
|
-
instances.
|
|
189
|
+
Args:
|
|
190
|
+
project (str): A string representing the name of the project.
|
|
191
|
+
instance (str): string representing the name of the instance.
|
|
192
|
+
pub_key (str): A string representing PEM-encoded RSA public key.
|
|
193
|
+
enable_iam_auth (bool): Enables automatic IAM database
|
|
194
|
+
authentication for Postgres or MySQL instances.
|
|
192
195
|
|
|
193
|
-
:
|
|
194
|
-
|
|
195
|
-
|
|
196
|
+
Returns:
|
|
197
|
+
A tuple containing an ephemeral certificate from
|
|
198
|
+
the Cloud SQL instance as well as a datetime object
|
|
199
|
+
representing the expiration time of the certificate.
|
|
196
200
|
"""
|
|
197
201
|
headers = {
|
|
198
202
|
"Authorization": f"Bearer {self._credentials.token}",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
+
import abc
|
|
17
18
|
from dataclasses import dataclass
|
|
18
19
|
import logging
|
|
19
20
|
import ssl
|
|
@@ -34,6 +35,27 @@ if TYPE_CHECKING:
|
|
|
34
35
|
logger = logging.getLogger(name=__name__)
|
|
35
36
|
|
|
36
37
|
|
|
38
|
+
class ConnectionInfoCache(abc.ABC):
|
|
39
|
+
"""Abstract class for Connector connection info caches."""
|
|
40
|
+
|
|
41
|
+
@abc.abstractmethod
|
|
42
|
+
async def connect_info(self) -> ConnectionInfo:
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
@abc.abstractmethod
|
|
46
|
+
async def force_refresh(self) -> None:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
@abc.abstractmethod
|
|
50
|
+
async def close(self) -> None:
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
@abc.abstractmethod
|
|
55
|
+
def closed(self) -> bool:
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
|
|
37
59
|
@dataclass
|
|
38
60
|
class ConnectionInfo:
|
|
39
61
|
"""Contains all necessary information to connect securely to the
|
|
@@ -19,6 +19,10 @@ import re
|
|
|
19
19
|
# Additionally, we have to support legacy "domain-scoped" projects
|
|
20
20
|
# (e.g. "google.com:PROJECT")
|
|
21
21
|
CONN_NAME_REGEX = re.compile(("([^:]+(:[^:]+)?):([^:]+):([^:]+)"))
|
|
22
|
+
# The domain name pattern in accordance with RFC 1035, RFC 1123 and RFC 2181.
|
|
23
|
+
DOMAIN_NAME_REGEX = re.compile(
|
|
24
|
+
r"^(?:[_a-z0-9](?:[_a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?)?$"
|
|
25
|
+
)
|
|
22
26
|
|
|
23
27
|
|
|
24
28
|
@dataclass
|
|
@@ -38,6 +42,16 @@ class ConnectionName:
|
|
|
38
42
|
return f"{self.domain_name} -> {self.project}:{self.region}:{self.instance_name}"
|
|
39
43
|
return f"{self.project}:{self.region}:{self.instance_name}"
|
|
40
44
|
|
|
45
|
+
def get_connection_string(self) -> str:
|
|
46
|
+
"""Get the instance connection string for the Cloud SQL instance."""
|
|
47
|
+
return f"{self.project}:{self.region}:{self.instance_name}"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _is_valid_domain(domain_name: str) -> bool:
|
|
51
|
+
if DOMAIN_NAME_REGEX.fullmatch(domain_name) is None:
|
|
52
|
+
return False
|
|
53
|
+
return True
|
|
54
|
+
|
|
41
55
|
|
|
42
56
|
def _parse_connection_name(connection_name: str) -> ConnectionName:
|
|
43
57
|
return _parse_connection_name_with_domain_name(connection_name, "")
|